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.
1532 lines
47 KiB
1532 lines
47 KiB
<?php declare(strict_types = 0);
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* User roles API implementation.
|
|
*/
|
|
class CRole extends CApiService {
|
|
|
|
public const ACCESS_RULES = [
|
|
'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
|
|
'create' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'update' => ['min_user_type' => USER_TYPE_SUPER_ADMIN],
|
|
'delete' => ['min_user_type' => USER_TYPE_SUPER_ADMIN]
|
|
];
|
|
|
|
protected $tableName = 'role';
|
|
protected $tableAlias = 'r';
|
|
protected $sortColumns = ['roleid', 'name'];
|
|
|
|
/**
|
|
* Rule types.
|
|
*/
|
|
private const RULE_TYPE_INT32 = 0;
|
|
private const RULE_TYPE_STR = 1;
|
|
private const RULE_TYPE_MODULE = 2;
|
|
private const RULE_TYPE_SERVICE = 3;
|
|
|
|
/**
|
|
* Rule type correspondence to the database field names.
|
|
*/
|
|
public const RULE_TYPE_FIELDS = [
|
|
self::RULE_TYPE_INT32 => 'value_int',
|
|
self::RULE_TYPE_STR => 'value_str',
|
|
self::RULE_TYPE_MODULE => 'value_moduleid',
|
|
self::RULE_TYPE_SERVICE => 'value_serviceid'
|
|
];
|
|
|
|
/**
|
|
* @param array $options
|
|
*
|
|
* @return array|int
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
public function get(array $options = []) {
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
// filter
|
|
'roleids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null],
|
|
'filter' => ['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['roleid', 'name', 'type', 'readonly']],
|
|
'search' => ['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['name']],
|
|
'searchByAny' => ['type' => API_BOOLEAN, 'default' => false],
|
|
'startSearch' => ['type' => API_FLAG, 'default' => false],
|
|
'excludeSearch' => ['type' => API_FLAG, 'default' => false],
|
|
'searchWildcardsEnabled' => ['type' => API_BOOLEAN, 'default' => false],
|
|
// output
|
|
'output' => ['type' => API_OUTPUT, 'in' => implode(',', ['roleid', 'name', 'type', 'readonly']), 'default' => API_OUTPUT_EXTEND],
|
|
'countOutput' => ['type' => API_FLAG, 'default' => false],
|
|
'selectRules' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', ['ui', 'ui.default_access', 'services.read.mode', 'services.read.list', 'services.read.tag', 'services.write.mode', 'services.write.list', 'services.write.tag', 'modules', 'modules.default_access', 'api.access', 'api.mode', 'api', 'actions', 'actions.default_access']), 'default' => null],
|
|
'selectUsers' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL | API_ALLOW_COUNT, 'in' => implode(',', ['userid', 'username', 'name', 'surname', 'url', 'autologin', 'autologout', 'lang', 'refresh', 'theme', 'attempt_failed', 'attempt_ip', 'attempt_clock', 'rows_per_page', 'timezone', 'roleid']), 'default' => null],
|
|
// sort and limit
|
|
'sortfield' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'in' => implode(',', $this->sortColumns), 'uniq' => true, 'default' => []],
|
|
'sortorder' => ['type' => API_SORTORDER, 'default' => []],
|
|
'limit' => ['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'in' => '1:'.ZBX_MAX_INT32, 'default' => null],
|
|
// flags
|
|
'editable' => ['type' => API_BOOLEAN, 'default' => false],
|
|
'preservekeys' => ['type' => API_BOOLEAN, 'default' => false]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $options, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
if ($options['editable'] && self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
return $options['countOutput'] ? '0' : [];
|
|
}
|
|
|
|
$db_roles = [];
|
|
|
|
$sql = $this->createSelectQuery('role', $options);
|
|
$resource = DBselect($sql, $options['limit']);
|
|
|
|
while ($row = DBfetch($resource)) {
|
|
if ($options['countOutput']) {
|
|
return $row['rowscount'];
|
|
}
|
|
|
|
$db_roles[$row['roleid']] = $row;
|
|
}
|
|
|
|
if ($db_roles) {
|
|
$db_roles = $this->addRelatedObjects($options, $db_roles);
|
|
$db_roles = $this->unsetExtraFields($db_roles, ['roleid', 'type'], $options['output']);
|
|
|
|
if (!$options['preservekeys']) {
|
|
$db_roles = array_values($db_roles);
|
|
}
|
|
}
|
|
|
|
return $db_roles;
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
public function create(array $roles): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'role', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
$this->validateCreate($roles);
|
|
|
|
$ins_roles = [];
|
|
|
|
foreach ($roles as $role) {
|
|
unset($role['rules']);
|
|
$ins_roles[] = $role;
|
|
}
|
|
|
|
$roleids = DB::insert('role', $ins_roles);
|
|
$roles = array_combine($roleids, $roles);
|
|
|
|
$this->updateRules($roles);
|
|
|
|
foreach ($roles as $roleid => &$role) {
|
|
$role['roleid'] = $roleid;
|
|
}
|
|
unset($role);
|
|
|
|
$this->addAuditBulk(CAudit::ACTION_ADD, CAudit::RESOURCE_USER_ROLE, $roles);
|
|
|
|
return ['roleids' => $roleids];
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function validateCreate(array &$roles): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('role', 'name')],
|
|
'type' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [USER_TYPE_ZABBIX_USER, USER_TYPE_ZABBIX_ADMIN, USER_TYPE_SUPER_ADMIN])],
|
|
'rules' => ['type' => API_OBJECT, 'default' => [], 'fields' => [
|
|
'ui' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'ui.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'services.read.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM.','.ZBX_ROLE_RULE_SERVICES_ACCESS_ALL],
|
|
'services.read.list' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'serviceid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'services.read.tag' => ['type' => API_OBJECT, 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('role_rule', 'value_str'), 'default' => '']
|
|
]],
|
|
'services.write.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM.','.ZBX_ROLE_RULE_SERVICES_ACCESS_ALL],
|
|
'services.write.list' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'serviceid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'services.write.tag' => ['type' => API_OBJECT, 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('role_rule', 'value_str'), 'default' => '']
|
|
]],
|
|
'modules' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'moduleid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'modules.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'api' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'uniq' => true],
|
|
'api.access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'api.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_API_MODE_DENY.','.ZBX_ROLE_RULE_API_MODE_ALLOW],
|
|
'actions' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'actions.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $roles, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$this->checkDuplicates($roles);
|
|
$this->checkRules($roles);
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
public function update(array $roles): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'role', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
$this->validateUpdate($roles, $db_roles);
|
|
|
|
$upd_roles = [];
|
|
|
|
foreach ($roles as $role) {
|
|
$upd_role = DB::getUpdatedValues('role', $role, $db_roles[$role['roleid']]);
|
|
|
|
if ($upd_role) {
|
|
$upd_roles[] = [
|
|
'values' => $upd_role,
|
|
'where' => ['roleid' => $role['roleid']]
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($upd_roles) {
|
|
DB::update('role', $upd_roles);
|
|
}
|
|
|
|
$roles = array_column($roles, null, 'roleid');
|
|
|
|
$this->updateRules($roles, $db_roles);
|
|
|
|
$this->addAuditBulk(CAudit::ACTION_UPDATE, CAudit::RESOURCE_USER_ROLE, $roles, $db_roles);
|
|
|
|
return ['roleids' => array_column($roles, 'roleid')];
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
* @param array|null $db_roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function validateUpdate(array &$roles, ?array &$db_roles): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
|
|
'roleid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('role', 'name')],
|
|
'type' => ['type' => API_INT32, 'in' => implode(',', [USER_TYPE_ZABBIX_USER, USER_TYPE_ZABBIX_ADMIN, USER_TYPE_SUPER_ADMIN])],
|
|
'rules' => ['type' => API_OBJECT, 'fields' => [
|
|
'ui' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'ui.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'services.read.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM.','.ZBX_ROLE_RULE_SERVICES_ACCESS_ALL],
|
|
'services.read.list' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'serviceid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'services.read.tag' => ['type' => API_OBJECT, 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('role_rule', 'value_str'), 'default' => '']
|
|
]],
|
|
'services.write.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM.','.ZBX_ROLE_RULE_SERVICES_ACCESS_ALL],
|
|
'services.write.list' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'serviceid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]],
|
|
'services.write.tag' => ['type' => API_OBJECT, 'fields' => [
|
|
'tag' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'value' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('role_rule', 'value_str'), 'default' => '']
|
|
]],
|
|
'modules' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'moduleid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'modules.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'api' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'uniq' => true],
|
|
'api.access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED],
|
|
'api.mode' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_API_MODE_DENY.','.ZBX_ROLE_RULE_API_MODE_ALLOW],
|
|
'actions' => ['type' => API_OBJECTS, 'flags' => API_NORMALIZE, 'fields' => [
|
|
'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('role_rule', 'value_str')],
|
|
'status' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED, 'default' => ZBX_ROLE_RULE_ENABLED]
|
|
]],
|
|
'actions.default_access' => ['type' => API_INT32, 'in' => ZBX_ROLE_RULE_DISABLED.','.ZBX_ROLE_RULE_ENABLED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $roles, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_roles = $this->get([
|
|
'output' => ['roleid', 'name', 'type', 'readonly'],
|
|
'roleids' => array_column($roles, 'roleid'),
|
|
'selectRules' => ['ui', 'ui.default_access', 'services.read.mode', 'services.read.list',
|
|
'services.read.tag', 'services.write.mode', 'services.write.list', 'services.write.tag', 'modules',
|
|
'modules.default_access', 'api.access', 'api.mode', 'api', 'actions', 'actions.default_access'
|
|
],
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_roles) != count($roles)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
$this->checkDuplicates($roles, $db_roles);
|
|
$this->checkRules($roles, $db_roles);
|
|
$this->checkReadonly($db_roles);
|
|
$this->checkOwnRoleType($roles);
|
|
}
|
|
|
|
/**
|
|
* @param array $roleids
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
public function delete(array $roleids): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'role', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $roleids, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_roles = $this->get([
|
|
'output' => ['roleid', 'name', 'readonly'],
|
|
'selectUsers' => ['userid'],
|
|
'roleids' => $roleids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($db_roles) != count($roleids)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
foreach ($db_roles as $db_role) {
|
|
if ($db_role['readonly'] == 1) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('Cannot delete readonly user role "%1$s".', $db_role['name'])
|
|
);
|
|
}
|
|
|
|
if ($db_role['users']) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('Cannot delete assigned user role "%1$s".', $db_role['name'])
|
|
);
|
|
}
|
|
}
|
|
|
|
DB::delete('role', ['roleid' => $roleids]);
|
|
|
|
$this->addAuditBulk(CAudit::ACTION_DELETE, CAudit::RESOURCE_USER_ROLE, $db_roles);
|
|
|
|
return ['roleids' => $roleids];
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
* @param array|null $db_roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkDuplicates(array $roles, array $db_roles = null): void {
|
|
$names = [];
|
|
|
|
foreach ($roles as $role) {
|
|
if ($db_roles === null
|
|
|| (array_key_exists('name', $role) && $role['name'] !== $db_roles[$role['roleid']]['name'])) {
|
|
$names[] = $role['name'];
|
|
}
|
|
}
|
|
|
|
if (!$names) {
|
|
return;
|
|
}
|
|
|
|
$duplicate = DB::select('role', [
|
|
'output' => ['name'],
|
|
'filter' => ['name' => $names],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($duplicate) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('User role "%1$s" already exists.', $duplicate[0]['name']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check user role rules.
|
|
*
|
|
* @param array $roles
|
|
* @param array|null $db_roles
|
|
*
|
|
* @throws APIException if input is invalid.
|
|
*/
|
|
private function checkRules(array $roles, array $db_roles = null): void {
|
|
foreach ($roles as $role) {
|
|
if (!array_key_exists('rules', $role)) {
|
|
continue;
|
|
}
|
|
|
|
$name = $db_roles !== null ? $db_roles[$role['roleid']]['name'] : $role['name'];
|
|
$type = array_key_exists('type', $role) ? $role['type'] : $db_roles[$role['roleid']]['type'];
|
|
|
|
$db_rules = $db_roles !== null ? $db_roles[$role['roleid']]['rules'] : null;
|
|
|
|
$this->checkUiRules($name, (int) $type, $role['rules'], $db_rules);
|
|
$this->checkServicesRules($name, (int) $type, $role['rules'], $db_rules);
|
|
$this->checkModulesRules($name, $role['rules']);
|
|
$this->checkApiRules($name, $role['rules']);
|
|
$this->checkActionsRules($name, (int) $type, $role['rules']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param int $type
|
|
* @param array $rules
|
|
* @param array|null $db_rules
|
|
|
|
* @throws APIException
|
|
*/
|
|
private function checkUiRules(string $name, int $type, array $rules, array $db_rules = null): void {
|
|
if (!array_key_exists('ui', $rules)) {
|
|
return;
|
|
}
|
|
|
|
if (array_key_exists('ui.default_access', $rules)) {
|
|
$default_access = $rules['ui.default_access'];
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$default_access = $db_rules['ui.default_access'];
|
|
}
|
|
else {
|
|
$default_access = ZBX_ROLE_RULE_ENABLED;
|
|
}
|
|
|
|
$ui_rules = [];
|
|
|
|
foreach (CRoleHelper::getUiElementsByUserType($type) as $ui_element) {
|
|
$ui_rule_name = substr($ui_element, strlen('ui.'));
|
|
$ui_rules[$ui_rule_name] = $default_access == ZBX_ROLE_RULE_ENABLED;
|
|
}
|
|
|
|
foreach ($rules['ui'] as $ui_rule) {
|
|
if (!array_key_exists($ui_rule['name'], $ui_rules)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('UI element "%2$s" is not available for user role "%1$s".', $name, $ui_rule['name'])
|
|
);
|
|
}
|
|
|
|
$ui_rules[$ui_rule['name']] = $ui_rule['status'] == ZBX_ROLE_RULE_ENABLED;
|
|
}
|
|
|
|
if (!in_array(true, $ui_rules)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('At least one UI element must be enabled for user role "%1$s".', $name)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param int $type
|
|
* @param array $rules
|
|
* @param array|null $db_rules
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkServicesRules(string $name, int $type, array $rules, array $db_rules = null): void {
|
|
$this->checkServicesReadRules($name, $rules, $db_rules);
|
|
$this->checkServicesWriteRules($name, $type, $rules, $db_rules);
|
|
|
|
$list = [];
|
|
|
|
if (array_key_exists('services.read.list', $rules)) {
|
|
$list = array_merge($list, $rules['services.read.list']);
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$list = array_merge($list, $db_rules['services.read.list']);
|
|
}
|
|
|
|
if (array_key_exists('services.write.list', $rules)) {
|
|
$list = array_merge($list, $rules['services.write.list']);
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$list = array_merge($list, $db_rules['services.write.list']);
|
|
}
|
|
|
|
if (!$list) {
|
|
return;
|
|
}
|
|
|
|
$serviceids = array_unique(array_column($list, 'serviceid'));
|
|
|
|
$db_services = DB::select('services', [
|
|
'output' => ['serviceid'],
|
|
'serviceids' => $serviceids,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$unavailable_serviceids = array_diff($serviceids, array_keys($db_services));
|
|
|
|
if ($unavailable_serviceids) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Service with ID "%2$s" is not available for user role "%1$s".', $name, $unavailable_serviceids[0])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $rules
|
|
* @param array|null $db_rules
|
|
|
|
* @throws APIException
|
|
*/
|
|
private function checkServicesReadRules(string $name, array $rules, array $db_rules = null): void {
|
|
if (!array_key_exists('services.read.mode', $rules)
|
|
&& !array_key_exists('services.read.list', $rules)
|
|
&& !array_key_exists('services.read.tag', $rules)) {
|
|
return;
|
|
}
|
|
|
|
if (array_key_exists('services.read.mode', $rules)) {
|
|
$mode = $rules['services.read.mode'];
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$mode = $db_rules['services.read.mode'];
|
|
}
|
|
else {
|
|
$mode = ZBX_ROLE_RULE_SERVICES_ACCESS_ALL;
|
|
}
|
|
|
|
if ($mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
if (array_key_exists('services.read.tag', $rules)) {
|
|
if ($rules['services.read.tag']['tag'] === '' && $rules['services.read.tag']['value'] !== '') {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-empty tag value while having empty tag in rule "%2$s" for user role "%1$s".',
|
|
$name, 'services.read.tag'
|
|
));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (array_key_exists('services.read.list', $rules)) {
|
|
$has_list = (bool) $rules['services.read.list'];
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$has_list = (bool) $db_rules['services.read.list'];
|
|
}
|
|
else {
|
|
$has_list = false;
|
|
}
|
|
|
|
if ($has_list) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-default "%2$s" rule while having "%3$s" set to %4$d for user role "%1$s".',
|
|
$name, 'services.read.list', 'services.read.mode', ZBX_ROLE_RULE_SERVICES_ACCESS_ALL
|
|
));
|
|
}
|
|
|
|
if (array_key_exists('services.read.tag', $rules)) {
|
|
$has_tag = $rules['services.read.tag']['tag'] !== '';
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$has_tag = $db_rules['services.read.tag']['tag'] !== '';
|
|
}
|
|
else {
|
|
$has_tag = false;
|
|
}
|
|
|
|
if ($has_tag) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-default "%2$s" rule while having "%3$s" set to %4$d for user role "%1$s".',
|
|
$name, 'services.read.tag', 'services.read.mode', ZBX_ROLE_RULE_SERVICES_ACCESS_ALL
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param int $type
|
|
* @param array $rules
|
|
* @param array|null $db_rules
|
|
|
|
* @throws APIException
|
|
*/
|
|
private function checkServicesWriteRules(string $name, int $type, array $rules, array $db_rules = null): void {
|
|
if (!array_key_exists('services.write.mode', $rules)
|
|
&& !array_key_exists('services.write.list', $rules)
|
|
&& !array_key_exists('services.write.tag', $rules)) {
|
|
return;
|
|
}
|
|
|
|
if (array_key_exists('services.write.mode', $rules)) {
|
|
$mode = $rules['services.write.mode'];
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$mode = $db_rules['services.write.mode'];
|
|
}
|
|
elseif (self::$userData['type'] == USER_TYPE_SUPER_ADMIN || self::$userData['type'] == USER_TYPE_ZABBIX_ADMIN) {
|
|
$mode = ZBX_ROLE_RULE_SERVICES_ACCESS_ALL;
|
|
}
|
|
else {
|
|
$mode = ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM;
|
|
}
|
|
|
|
if ($mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
if (array_key_exists('services.write.tag', $rules)) {
|
|
if ($rules['services.write.tag']['tag'] === '' && $rules['services.write.tag']['value'] !== '') {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-empty tag value while having empty tag in rule "%2$s" for user role "%1$s".',
|
|
$name, 'services.write.tag'
|
|
));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (array_key_exists('services.write.list', $rules)) {
|
|
$has_list = (bool) $rules['services.write.list'];
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$has_list = (bool) $db_rules['services.write.list'];
|
|
}
|
|
else {
|
|
$has_list = false;
|
|
}
|
|
|
|
if ($has_list) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-default "%2$s" rule while having "%3$s" set to %4$d for user role "%1$s".',
|
|
$name, 'services.write.list', 'services.write.mode', ZBX_ROLE_RULE_SERVICES_ACCESS_ALL
|
|
));
|
|
}
|
|
|
|
if (array_key_exists('services.write.tag', $rules)) {
|
|
$has_tag = $rules['services.write.tag']['tag'] !== '';
|
|
}
|
|
elseif ($db_rules !== null) {
|
|
$has_tag = $db_rules['services.write.tag']['tag'] !== '';
|
|
}
|
|
else {
|
|
$has_tag = false;
|
|
}
|
|
|
|
if ($has_tag) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s(
|
|
'Cannot have non-default "%2$s" rule while having "%3$s" set to %4$d for user role "%1$s".',
|
|
$name, 'services.write.tag', 'services.write.mode', ZBX_ROLE_RULE_SERVICES_ACCESS_ALL
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $rules
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkModulesRules(string $name, array $rules): void {
|
|
if (!array_key_exists('modules', $rules)) {
|
|
return;
|
|
}
|
|
|
|
$moduleids = [];
|
|
|
|
foreach ($rules['modules'] as $module) {
|
|
$moduleids[$module['moduleid']] = true;
|
|
}
|
|
|
|
if (!$moduleids) {
|
|
return;
|
|
}
|
|
|
|
$unavailable_moduleids = array_diff(array_keys($moduleids), self::getModuleIds());
|
|
|
|
if ($unavailable_moduleids) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Module with ID "%2$s" is not available for user role "%1$s".', $name, $unavailable_moduleids[0])
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $rules
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkApiRules(string $name, array $rules): void {
|
|
if (!array_key_exists('api', $rules)) {
|
|
return;
|
|
}
|
|
|
|
foreach ($rules['api'] as $rule) {
|
|
if ($rule === ZBX_ROLE_RULE_API_WILDCARD || $rule === ZBX_ROLE_RULE_API_WILDCARD_ALIAS) {
|
|
continue;
|
|
}
|
|
|
|
if (!in_array($rule, CRoleHelper::getApiMethodMasks(USER_TYPE_SUPER_ADMIN), true)
|
|
&& !in_array($rule, CRoleHelper::getApiMethods(USER_TYPE_SUPER_ADMIN), true)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Invalid API method "%2$s" for user role "%1$s".', $name, $rule)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param int $type
|
|
* @param array $rules
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkActionsRules(string $name, int $type, array $rules): void {
|
|
if (!array_key_exists('actions', $rules)) {
|
|
return;
|
|
}
|
|
|
|
$all_actions = CRoleHelper::getActionsByUserType($type);
|
|
|
|
foreach ($rules['actions'] as $rule) {
|
|
if (!in_array('actions.'.$rule['name'], $all_actions)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Action "%2$s" is not available for user role "%1$s".', $name, $rule['name'])
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $db_roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkReadonly(array $db_roles): void {
|
|
foreach ($db_roles as $db_role) {
|
|
if ($db_role['readonly'] == 1) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _s('Cannot update readonly user role "%1$s".',
|
|
$db_role['name']
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function checkOwnRoleType(array $roles): void {
|
|
$role_types = array_column($roles, 'type', 'roleid');
|
|
|
|
if (array_key_exists(self::$userData['roleid'], $role_types)
|
|
&& $role_types[self::$userData['roleid']] != self::$userData['type']) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('Cannot change the user type of own role.'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $roles
|
|
* @param array|null $db_roles
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function updateRules(array $roles, array $db_roles = null): void {
|
|
$default_rules = [
|
|
'ui' => [],
|
|
'ui.default_access' => ZBX_ROLE_RULE_ENABLED,
|
|
'services.read.mode' => ZBX_ROLE_RULE_SERVICES_ACCESS_ALL,
|
|
'services.read.list' => [],
|
|
'services.read.tag' => ['tag' => '', 'value' => ''],
|
|
'services.write.mode' => ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM,
|
|
'services.write.list' => [],
|
|
'services.write.tag' => ['tag' => '', 'value' => ''],
|
|
'modules' => [],
|
|
'modules.default_access' => ZBX_ROLE_RULE_ENABLED,
|
|
'api' => [],
|
|
'api.access' => ZBX_ROLE_RULE_ENABLED,
|
|
'api.mode' => ZBX_ROLE_RULE_API_MODE_DENY,
|
|
'actions' => [],
|
|
'actions.default_access' => ZBX_ROLE_RULE_ENABLED
|
|
];
|
|
|
|
$rules = [];
|
|
|
|
foreach ($roles as $roleid => $role) {
|
|
if (!array_key_exists('rules', $role)) {
|
|
continue;
|
|
}
|
|
|
|
$type = array_key_exists('type', $role) ? $role['type'] : $db_roles[$role['roleid']]['type'];
|
|
|
|
$old_rules = $db_roles !== null ? $db_roles[$roleid]['rules'] : $default_rules;
|
|
$new_rules = $role['rules'] + $old_rules;
|
|
|
|
$rules[$roleid] = array_merge(
|
|
$this->compileUiRules((int) $type, $old_rules, $new_rules),
|
|
$this->compileServicesReadRules($new_rules),
|
|
$this->compileServicesWriteRules($new_rules),
|
|
$this->compileModulesRules($old_rules, $new_rules),
|
|
$this->compileApiRules($new_rules),
|
|
$this->compileActionsRules((int) $type, $old_rules, $new_rules)
|
|
);
|
|
}
|
|
|
|
$del_rules = [];
|
|
$ins_rules = [];
|
|
|
|
if ($db_roles !== null) {
|
|
$db_rules = DB::select('role_rule', [
|
|
'output' => ['role_ruleid', 'roleid', 'type', 'name', 'value_int', 'value_str', 'value_moduleid',
|
|
'value_serviceid'
|
|
],
|
|
'filter' => ['roleid' => array_keys($rules)]
|
|
]);
|
|
|
|
foreach ($db_rules as $db_rule) {
|
|
$value = $db_rule[self::RULE_TYPE_FIELDS[$db_rule['type']]];
|
|
|
|
$del_rules[$db_rule['roleid']][$db_rule['name']][$db_rule['type']][$value] = $db_rule['role_ruleid'];
|
|
}
|
|
}
|
|
|
|
foreach ($rules as $roleid => $role_rules) {
|
|
foreach ($role_rules as $rule) {
|
|
if (array_key_exists($roleid, $del_rules)
|
|
&& array_key_exists($rule['name'], $del_rules[$roleid])
|
|
&& array_key_exists($rule['type'], $del_rules[$roleid][$rule['name']])
|
|
&& array_key_exists($rule['value'], $del_rules[$roleid][$rule['name']][$rule['type']])) {
|
|
unset($del_rules[$roleid][$rule['name']][$rule['type']][$rule['value']]);
|
|
}
|
|
else {
|
|
$ins_rules[$rule['type']][] = [
|
|
'roleid' => $roleid,
|
|
'type' => $rule['type'],
|
|
'name' => $rule['name'],
|
|
self::RULE_TYPE_FIELDS[$rule['type']] => $rule['value']
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($del_rules) {
|
|
$del_role_ruleids = [];
|
|
|
|
foreach ($del_rules as $del_rules) {
|
|
foreach ($del_rules as $del_rules) {
|
|
foreach ($del_rules as $del_rules) {
|
|
foreach ($del_rules as $role_ruleid) {
|
|
$del_role_ruleids[$role_ruleid] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DB::delete('role_rule', ['role_ruleid' => array_keys($del_role_ruleids)]);
|
|
}
|
|
|
|
if ($ins_rules) {
|
|
foreach ($ins_rules as $ins_rules) {
|
|
DB::insertBatch('role_rule', $ins_rules);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param int $type
|
|
* @param array $old_rules
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*/
|
|
private function compileUiRules(int $type, array $old_rules, array $new_rules): array {
|
|
$old_ui_rules = array_column($old_rules['ui'], null, 'name');
|
|
$new_ui_rules = array_column($new_rules['ui'], null, 'name');
|
|
|
|
$compiled_rules = [];
|
|
|
|
foreach (CRoleHelper::getUiElementsByUserType($type) as $ui_rule_name) {
|
|
$ui_element = substr($ui_rule_name, strlen('ui.'));
|
|
|
|
if (array_key_exists($ui_element, $new_ui_rules)) {
|
|
$ui_rule_status = $new_ui_rules[$ui_element]['status'];
|
|
}
|
|
elseif (array_key_exists($ui_element, $old_ui_rules)) {
|
|
$ui_rule_status = $old_ui_rules[$ui_element]['status'];
|
|
}
|
|
else {
|
|
$ui_rule_status = $old_rules['ui.default_access'];
|
|
}
|
|
|
|
if ($ui_rule_status != $new_rules['ui.default_access']) {
|
|
$compiled_rules[] = [
|
|
'name' => $ui_rule_name,
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $ui_rule_status
|
|
];
|
|
}
|
|
|
|
}
|
|
|
|
$compiled_rules[] = [
|
|
'name' => 'ui.default_access',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['ui.default_access']
|
|
];
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*/
|
|
private function compileServicesReadRules(array $new_rules): array {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.read',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['services.read.mode']
|
|
];
|
|
|
|
if ($new_rules['services.read.mode'] == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
foreach ($new_rules['services.read.list'] as $index => $service) {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.read.id.'.$index,
|
|
'type' => self::RULE_TYPE_SERVICE,
|
|
'value' => $service['serviceid']
|
|
];
|
|
}
|
|
|
|
if ($new_rules['services.read.tag']['tag'] !== '') {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.read.tag.name',
|
|
'type' => self::RULE_TYPE_STR,
|
|
'value' => $new_rules['services.read.tag']['tag']
|
|
];
|
|
|
|
if ($new_rules['services.read.tag']['value'] !== '') {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.read.tag.value',
|
|
'type' => self::RULE_TYPE_STR,
|
|
'value' => $new_rules['services.read.tag']['value']
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*/
|
|
private function compileServicesWriteRules(array $new_rules): array {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.write',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['services.write.mode']
|
|
];
|
|
|
|
if ($new_rules['services.write.mode'] == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
foreach ($new_rules['services.write.list'] as $index => $service) {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.write.id.'.$index,
|
|
'type' => self::RULE_TYPE_SERVICE,
|
|
'value' => $service['serviceid']
|
|
];
|
|
}
|
|
|
|
if ($new_rules['services.write.tag']['tag'] !== '') {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.write.tag.name',
|
|
'type' => self::RULE_TYPE_STR,
|
|
'value' => $new_rules['services.write.tag']['tag']
|
|
];
|
|
|
|
if ($new_rules['services.write.tag']['value'] !== '') {
|
|
$compiled_rules[] = [
|
|
'name' => 'services.write.tag.value',
|
|
'type' => self::RULE_TYPE_STR,
|
|
'value' => $new_rules['services.write.tag']['value']
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param array $old_rules
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function compileModulesRules(array $old_rules, array $new_rules): array {
|
|
$old_modules_rules = array_column($old_rules['modules'], null, 'moduleid');
|
|
$new_modules_rules = array_column($new_rules['modules'], null, 'moduleid');
|
|
|
|
$compiled_rules = [];
|
|
|
|
$index = 0;
|
|
|
|
foreach (self::getModuleIds() as $moduleid) {
|
|
if (array_key_exists($moduleid, $new_modules_rules)) {
|
|
$module_status = $new_modules_rules[$moduleid]['status'];
|
|
}
|
|
elseif (array_key_exists($moduleid, $old_modules_rules)) {
|
|
$module_status = $old_modules_rules[$moduleid]['status'];
|
|
}
|
|
else {
|
|
$module_status = $old_rules['modules.default_access'];
|
|
}
|
|
|
|
if ($module_status != $new_rules['modules.default_access']) {
|
|
$compiled_rules[] = [
|
|
'name' => 'modules.module.'.$index,
|
|
'type' => self::RULE_TYPE_MODULE,
|
|
'value' => $moduleid
|
|
];
|
|
|
|
$index++;
|
|
}
|
|
}
|
|
|
|
$compiled_rules[] = [
|
|
'name' => 'modules.default_access',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['modules.default_access']
|
|
];
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*/
|
|
private function compileApiRules(array $new_rules): array {
|
|
$compiled_rules = [];
|
|
|
|
$compiled_rules[] = [
|
|
'name' => 'api.access',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['api.access']
|
|
];
|
|
|
|
if ($new_rules['api.access'] == ZBX_ROLE_RULE_ENABLED) {
|
|
$compiled_rules[] = [
|
|
'name' => 'api.mode',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['api.mode']
|
|
];
|
|
|
|
foreach ($new_rules['api'] as $index => $api_method) {
|
|
$compiled_rules[] = [
|
|
'name' => 'api.method.'.$index,
|
|
'type' => self::RULE_TYPE_STR,
|
|
'value' => $api_method
|
|
];
|
|
}
|
|
}
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param int $type
|
|
* @param array $old_rules
|
|
* @param array $new_rules
|
|
*
|
|
* @return array
|
|
*/
|
|
private function compileActionsRules(int $type, array $old_rules, array $new_rules): array {
|
|
$old_actions_rules = array_column($old_rules['actions'], null, 'name');
|
|
$new_actions_rules = array_column($new_rules['actions'], null, 'name');
|
|
|
|
$compiled_rules = [];
|
|
|
|
foreach (CRoleHelper::getActionsByUserType($type) as $action_rule_name) {
|
|
$action_element = substr($action_rule_name, strlen('actions.'));
|
|
|
|
if (array_key_exists($action_element, $new_actions_rules)) {
|
|
$action_rule_status = $new_actions_rules[$action_element]['status'];
|
|
}
|
|
elseif (array_key_exists($action_element, $old_actions_rules)) {
|
|
$action_rule_status = $old_actions_rules[$action_element]['status'];
|
|
}
|
|
else {
|
|
$action_rule_status = $old_rules['actions.default_access'];
|
|
}
|
|
|
|
if ($action_rule_status != $new_rules['actions.default_access']) {
|
|
$compiled_rules[] = [
|
|
'name' => $action_rule_name,
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $action_rule_status
|
|
];
|
|
}
|
|
|
|
}
|
|
|
|
$compiled_rules[] = [
|
|
'name' => 'actions.default_access',
|
|
'type' => self::RULE_TYPE_INT32,
|
|
'value' => $new_rules['actions.default_access']
|
|
];
|
|
|
|
return $compiled_rules;
|
|
}
|
|
|
|
/**
|
|
* @param string $table_name
|
|
* @param string $table_alias
|
|
* @param array $options
|
|
* @param array $sql_parts
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function applyQueryFilterOptions($table_name, $table_alias, array $options, array $sql_parts): array {
|
|
$sql_parts = parent::applyQueryFilterOptions($table_name, $table_alias, $options, $sql_parts);
|
|
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
$sql_parts['from']['users'] = 'users u';
|
|
$sql_parts['where']['u'] = 'r.roleid=u.roleid';
|
|
$sql_parts['where'][] = 'u.userid='.self::$userData['userid'];
|
|
}
|
|
|
|
return $sql_parts;
|
|
}
|
|
|
|
/**
|
|
* @param string $table_name
|
|
* @param string $table_alias
|
|
* @param array $options
|
|
* @param array $sql_parts
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function applyQueryOutputOptions($table_name, $table_alias, array $options, array $sql_parts): array {
|
|
$sql_parts = parent::applyQueryOutputOptions($table_name, $table_alias, $options, $sql_parts);
|
|
|
|
if (!$options['countOutput'] && $options['selectRules'] !== null) {
|
|
$sql_parts = $this->addQuerySelect('r.type', $sql_parts);
|
|
}
|
|
|
|
return $sql_parts;
|
|
}
|
|
|
|
/**
|
|
* @param array $options
|
|
* @param array $result
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
protected function addRelatedObjects(array $options, array $result): array {
|
|
$result = parent::addRelatedObjects($options, $result);
|
|
|
|
$roleids = array_keys($result);
|
|
|
|
if ($options['selectRules'] !== null) {
|
|
if ($options['selectRules'] === API_OUTPUT_EXTEND) {
|
|
$output = ['ui', 'ui.default_access', 'services.read.mode', 'services.read.list', 'services.read.tag',
|
|
'services.write.mode', 'services.write.list', 'services.write.tag', 'modules',
|
|
'modules.default_access', 'api', 'api.access', 'api.mode', 'actions', 'actions.default_access'
|
|
];
|
|
}
|
|
else {
|
|
$output = $options['selectRules'];
|
|
}
|
|
|
|
$rules = DB::select('role_rule', [
|
|
'output' => ['role_ruleid', 'roleid', 'type', 'name', 'value_int', 'value_str', 'value_moduleid',
|
|
'value_serviceid'
|
|
],
|
|
'filter' => ['roleid' => $roleids]
|
|
]);
|
|
|
|
$roles_rules = array_fill_keys($roleids, []);
|
|
|
|
foreach ($rules as $rule) {
|
|
$roles_rules[$rule['roleid']][$rule['name']] = $rule[self::RULE_TYPE_FIELDS[$rule['type']]];
|
|
}
|
|
|
|
foreach ($result as $roleid => &$role) {
|
|
$role['rules'] = array_merge(
|
|
$this->getRelatedUiRules($roles_rules[$roleid], $output, (int) $role['type']),
|
|
$this->getRelatedServicesReadRules($roles_rules[$roleid], $output),
|
|
$this->getRelatedServicesWriteRules($roles_rules[$roleid], $output),
|
|
$this->getRelatedModulesRules($roles_rules[$roleid], $output),
|
|
$this->getRelatedApiRules($roles_rules[$roleid], $output),
|
|
$this->getRelatedActionsRules($roles_rules[$roleid], $output, (int) $role['type'])
|
|
);
|
|
}
|
|
unset($role);
|
|
}
|
|
|
|
if ($options['selectUsers'] !== null) {
|
|
if ($options['selectUsers'] === API_OUTPUT_COUNT) {
|
|
$output = ['userid', 'roleid'];
|
|
}
|
|
elseif ($options['selectUsers'] === API_OUTPUT_EXTEND) {
|
|
$output = ['userid', 'username', 'name', 'surname', 'url', 'autologin', 'autologout', 'lang', 'refresh',
|
|
'theme', 'attempt_failed', 'attempt_ip', 'attempt_clock', 'rows_per_page', 'timezone', 'roleid'
|
|
];
|
|
}
|
|
else {
|
|
$output = array_unique(array_merge(['userid', 'roleid'], $options['selectUsers']));
|
|
}
|
|
|
|
$users = DB::select('users', [
|
|
'output' => $output,
|
|
'filter' => ['roleid' => $roleids],
|
|
'preservekeys' => true
|
|
]);
|
|
$relation_map = $this->createRelationMap($users, 'roleid', 'userid');
|
|
$users = $this->unsetExtraFields($users, ['userid', 'roleid'], $options['selectUsers']);
|
|
$result = $relation_map->mapMany($result, $users, 'users');
|
|
|
|
if ($options['selectUsers'] === API_OUTPUT_COUNT) {
|
|
foreach ($result as &$row) {
|
|
$row['users'] = (string) count($row['users']);
|
|
}
|
|
unset($row);
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
* @param int $type
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getRelatedUiRules(array $rules, array $output, int $type): array {
|
|
$ui_default_access = array_key_exists('ui.default_access', $rules)
|
|
? $rules['ui.default_access']
|
|
: (string) ZBX_ROLE_RULE_ENABLED;
|
|
|
|
$result = [];
|
|
|
|
if (in_array('ui', $output, true)) {
|
|
$ui = array_fill_keys(CRoleHelper::getUiElementsByUserType($type), $ui_default_access);
|
|
$ui = array_intersect_key($rules, $ui) + $ui;
|
|
|
|
$result['ui'] = [];
|
|
|
|
foreach ($ui as $ui_element => $status) {
|
|
$result['ui'][] = [
|
|
'name' => substr($ui_element, strlen('ui.')),
|
|
'status' => $status
|
|
];
|
|
}
|
|
}
|
|
|
|
if (in_array('ui.default_access', $output, true)) {
|
|
$result['ui.default_access'] = $ui_default_access;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getRelatedServicesReadRules(array $rules, array $output): array {
|
|
$result = [];
|
|
|
|
$services_read_mode = array_key_exists('services.read', $rules)
|
|
? $rules['services.read']
|
|
: (string) ZBX_ROLE_RULE_SERVICES_ACCESS_ALL;
|
|
|
|
if (in_array('services.read.mode', $output, true)) {
|
|
$result['services.read.mode'] = $services_read_mode;
|
|
}
|
|
|
|
if (in_array('services.read.list', $output, true)) {
|
|
$result['services.read.list'] = [];
|
|
|
|
if ($services_read_mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
$enum = 'services.read.id.';
|
|
|
|
foreach ($rules as $rule_name => $rule_value) {
|
|
if (strpos($rule_name, $enum) === 0) {
|
|
$result['services.read.list'][] = ['serviceid' => $rule_value];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_array('services.read.tag', $output, true)) {
|
|
$result['services.read.tag'] = ['tag' => '', 'value' => ''];
|
|
|
|
if ($services_read_mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
if (array_key_exists('services.read.tag.name', $rules)) {
|
|
$result['services.read.tag']['tag'] = $rules['services.read.tag.name'];
|
|
}
|
|
|
|
if (array_key_exists('services.read.tag.value', $rules)
|
|
&& $result['services.read.tag']['tag'] !== '') {
|
|
$result['services.read.tag']['value'] = $rules['services.read.tag.value'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getRelatedServicesWriteRules(array $rules, array $output): array {
|
|
$result = [];
|
|
|
|
$services_write_mode = array_key_exists('services.write', $rules)
|
|
? $rules['services.write']
|
|
: (string) ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM;
|
|
|
|
if (in_array('services.write.mode', $output, true)) {
|
|
$result['services.write.mode'] = $services_write_mode;
|
|
}
|
|
|
|
if (in_array('services.write.list', $output, true)) {
|
|
$result['services.write.list'] = [];
|
|
|
|
if ($services_write_mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
$enum = 'services.write.id.';
|
|
|
|
foreach ($rules as $rule_name => $rule_value) {
|
|
if (strpos($rule_name, $enum) === 0) {
|
|
$result['services.write.list'][] = ['serviceid' => $rule_value];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_array('services.write.tag', $output, true)) {
|
|
$result['services.write.tag'] = ['tag' => '', 'value' => ''];
|
|
|
|
if ($services_write_mode == ZBX_ROLE_RULE_SERVICES_ACCESS_CUSTOM) {
|
|
if (array_key_exists('services.write.tag.name', $rules)) {
|
|
$result['services.write.tag']['tag'] = $rules['services.write.tag.name'];
|
|
}
|
|
|
|
if (array_key_exists('services.write.tag.value', $rules)
|
|
&& $result['services.write.tag']['tag'] !== '') {
|
|
$result['services.write.tag']['value'] = $rules['services.write.tag.value'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
*
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function getRelatedModulesRules(array $rules, array $output): array {
|
|
$modules_default_access = array_key_exists('modules.default_access', $rules)
|
|
? $rules['modules.default_access']
|
|
: (string) ZBX_ROLE_RULE_ENABLED;
|
|
|
|
$result = [];
|
|
|
|
if (in_array('modules', $output, true)) {
|
|
$modules = [];
|
|
|
|
foreach (self::getModuleIds() as $moduleid) {
|
|
$modules[$moduleid] = [
|
|
'moduleid' => $moduleid,
|
|
'status' => $modules_default_access
|
|
];
|
|
}
|
|
|
|
$enum = 'modules.module.';
|
|
|
|
foreach ($rules as $rule_name => $rule_value) {
|
|
if (array_key_exists($rule_value, $modules) && strpos($rule_name, $enum) === 0) {
|
|
$modules[$rule_value]['status'] = $modules_default_access == ZBX_ROLE_RULE_ENABLED
|
|
? (string) ZBX_ROLE_RULE_DISABLED
|
|
: (string) ZBX_ROLE_RULE_ENABLED;
|
|
}
|
|
}
|
|
|
|
$result['modules'] = array_values($modules);
|
|
}
|
|
|
|
if (in_array('modules.default_access', $output, true)) {
|
|
$result['modules.default_access'] = $modules_default_access;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getRelatedApiRules(array $rules, array $output): array {
|
|
$result = [];
|
|
|
|
if (in_array('api.access', $output, true)) {
|
|
$result['api.access'] = array_key_exists('api.access', $rules)
|
|
? $rules['api.access']
|
|
: (string) ZBX_ROLE_RULE_ENABLED;
|
|
}
|
|
|
|
if (in_array('api.mode', $output, true)) {
|
|
$result['api.mode'] = array_key_exists('api.mode', $rules)
|
|
? $rules['api.mode']
|
|
: (string) ZBX_ROLE_RULE_API_MODE_DENY;
|
|
}
|
|
|
|
if (in_array('api', $output, true)) {
|
|
$result['api'] = [];
|
|
|
|
$enum = 'api.method.';
|
|
|
|
foreach ($rules as $rule_name => $rule_value) {
|
|
if (strpos($rule_name, $enum) === 0) {
|
|
$result['api'][] = $rule_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $rules
|
|
* @param array $output
|
|
* @param int $type
|
|
*
|
|
* @return array
|
|
*/
|
|
private function getRelatedActionsRules(array $rules, array $output, int $type): array {
|
|
$actions_default_access = array_key_exists('actions.default_access', $rules)
|
|
? $rules['actions.default_access']
|
|
: (string) ZBX_ROLE_RULE_ENABLED;
|
|
|
|
$result = [];
|
|
|
|
if (in_array('actions', $output, true)) {
|
|
$actions = array_fill_keys(CRoleHelper::getActionsByUserType($type), $actions_default_access);
|
|
$actions = array_intersect_key($rules, $actions) + $actions;
|
|
|
|
$result['actions'] = [];
|
|
|
|
foreach ($actions as $action => $status) {
|
|
$result['actions'][] = [
|
|
'name' => substr($action, strlen('actions.')),
|
|
'status' => $status
|
|
];
|
|
}
|
|
}
|
|
|
|
if (in_array('actions.default_access', $output, true)) {
|
|
$result['actions.default_access'] = $actions_default_access;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function getModuleIds(): array {
|
|
$modules = API::getApiService('module')->get([
|
|
'output' => [],
|
|
'preservekeys' => true
|
|
], false);
|
|
|
|
return array_keys($modules);
|
|
}
|
|
}
|