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.
809 lines
32 KiB
809 lines
32 KiB
<?php
|
|
/*
|
|
** Zabbix
|
|
** Copyright (C) 2001-2023 Zabbix SIA
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation; either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Class containing methods for operations with proxies.
|
|
*/
|
|
class CProxy 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 = 'proxy';
|
|
protected $tableAlias = 'p';
|
|
protected $sortColumns = ['proxyid', 'name', 'operating_mode'];
|
|
|
|
public const OUTPUT_FIELDS = ['proxyid', 'name', 'operating_mode', 'description', 'allowed_addresses', 'address',
|
|
'port', 'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject', 'lastaccess', 'version', 'compatibility'
|
|
];
|
|
|
|
/**
|
|
* @param array $options
|
|
*
|
|
* @throws APIException
|
|
*
|
|
* @return array|string
|
|
*/
|
|
public function get(array $options = []) {
|
|
$output_fields = self::OUTPUT_FIELDS;
|
|
|
|
/*
|
|
* For internal calls, it is possible to get the write-only fields if they were specified in output.
|
|
* Specify write-only fields in output only if they will not appear in debug mode.
|
|
*/
|
|
if (APP::getMode() !== APP::EXEC_MODE_API) {
|
|
$output_fields[] = 'tls_psk_identity';
|
|
$output_fields[] = 'tls_psk';
|
|
}
|
|
|
|
$api_input_rules = ['type' => API_OBJECT, 'fields' => [
|
|
'proxyids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null],
|
|
'filter' => ['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['name', 'operating_mode', 'lastaccess', 'version', 'compatibility']],
|
|
'search' => ['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['name', 'description']],
|
|
'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(',', $output_fields), 'default' => API_OUTPUT_EXTEND],
|
|
'countOutput' => ['type' => API_FLAG, 'default' => false],
|
|
'selectHosts' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', array_diff(CHost::OUTPUT_FIELDS, ['proxyid'])), '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],
|
|
'nopermissions' => ['type' => API_BOOLEAN, 'default' => false]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $options, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$sql_parts = [
|
|
'select' => ['proxyid' => 'p.proxyid'],
|
|
'from' => ['proxy' => 'proxy p'],
|
|
'where' => [],
|
|
'order' => []
|
|
];
|
|
|
|
// editable + PERMISSION CHECK
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN && !$options['nopermissions']) {
|
|
$permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ;
|
|
|
|
if ($permission == PERM_READ_WRITE) {
|
|
return $options['countOutput'] ? '0' : [];
|
|
}
|
|
}
|
|
|
|
$count_output = $options['countOutput'];
|
|
|
|
if ($count_output) {
|
|
$options['output'] = ['proxyid'];
|
|
$options['countOutput'] = false;
|
|
}
|
|
elseif ($options['output'] === API_OUTPUT_EXTEND) {
|
|
$options['output'] = $output_fields;
|
|
}
|
|
|
|
// proxyids
|
|
if ($options['proxyids'] !== null) {
|
|
$sql_parts['where'][] = dbConditionId('p.proxyid', $options['proxyids']);
|
|
}
|
|
|
|
// filter
|
|
if ($options['filter'] === null) {
|
|
$options['filter'] = [];
|
|
}
|
|
|
|
$this->dbFilter('proxy p', $options, $sql_parts);
|
|
|
|
$rt_filter = [];
|
|
foreach (['lastaccess', 'version', 'compatibility'] as $field) {
|
|
if (array_key_exists($field, $options['filter']) && $options['filter'][$field] !== null) {
|
|
$rt_filter[$field] = $options['filter'][$field];
|
|
}
|
|
}
|
|
|
|
if ($rt_filter) {
|
|
$this->dbFilter('proxy_rtdata pr', ['filter' => $rt_filter] + $options, $sql_parts);
|
|
}
|
|
|
|
// search
|
|
if ($options['search'] !== null) {
|
|
zbx_db_search('proxy p', $options, $sql_parts);
|
|
}
|
|
|
|
$sql_parts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sql_parts);
|
|
$sql_parts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sql_parts);
|
|
$resource = DBselect(self::createSelectQueryFromParts($sql_parts), $options['limit']);
|
|
|
|
$db_proxies = [];
|
|
|
|
while ($row = DBfetch($resource)) {
|
|
$db_proxies[$row['proxyid']] = $row;
|
|
}
|
|
|
|
if ($count_output) {
|
|
return (string) count($db_proxies);
|
|
}
|
|
|
|
if ($db_proxies) {
|
|
$db_proxies = $this->addRelatedObjects($options, $db_proxies);
|
|
$db_proxies = $this->unsetExtraFields($db_proxies, ['proxyid'], $options['output']);
|
|
|
|
if (!$options['preservekeys']) {
|
|
$db_proxies = array_values($db_proxies);
|
|
}
|
|
}
|
|
|
|
return $db_proxies;
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
*
|
|
* @throws APIException
|
|
*
|
|
* @return array
|
|
*/
|
|
public function create(array $proxies): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'proxy', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
self::validateCreate($proxies);
|
|
|
|
$proxyids = DB::insert('proxy', $proxies);
|
|
$proxy_rtdata = [];
|
|
|
|
foreach ($proxies as $index => &$proxy) {
|
|
$proxy['proxyid'] = $proxyids[$index];
|
|
$proxy_rtdata[] = ['proxyid' => $proxyids[$index]];
|
|
}
|
|
unset($proxy);
|
|
|
|
DB::insert('proxy_rtdata', $proxy_rtdata, false);
|
|
self::updateHosts($proxies);
|
|
|
|
self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_PROXY, $proxies);
|
|
|
|
return ['proxyids' => $proxyids];
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
*
|
|
* @throws APIException
|
|
*
|
|
* @return array
|
|
*/
|
|
public function update(array $proxies): array {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'proxy', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
$this->validateUpdate($proxies, $db_proxies);
|
|
|
|
self::addFieldDefaultsByTls($proxies, $db_proxies);
|
|
|
|
$upd_proxies = [];
|
|
|
|
foreach ($proxies as $proxy) {
|
|
$upd_proxy = DB::getUpdatedValues('proxy', $proxy, $db_proxies[$proxy['proxyid']]);
|
|
|
|
if ($upd_proxy) {
|
|
$upd_proxies[] = [
|
|
'values' => $upd_proxy,
|
|
'where' => ['proxyid' => $proxy['proxyid']]
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($upd_proxies) {
|
|
DB::update('proxy', $upd_proxies);
|
|
}
|
|
|
|
self::updateHosts($proxies, $db_proxies);
|
|
|
|
self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_PROXY, $proxies, $db_proxies);
|
|
|
|
return ['proxyids' => array_column($proxies, 'proxyid')];
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
* @param array|null $db_proxies
|
|
*/
|
|
private static function updateHosts(array &$proxies, array $db_proxies = null): void {
|
|
$upd_hosts = [];
|
|
|
|
foreach ($proxies as &$proxy) {
|
|
if (!array_key_exists('hosts', $proxy)) {
|
|
continue;
|
|
}
|
|
|
|
$db_hosts = $db_proxies !== null ? $db_proxies[$proxy['proxyid']]['hosts'] : [];
|
|
|
|
foreach ($proxy['hosts'] as $host) {
|
|
if (!array_key_exists($host['hostid'], $db_hosts)) {
|
|
$upd_hosts[$host['hostid']] = [
|
|
'values' => ['proxyid' => $proxy['proxyid']],
|
|
'where' => ['hostid' => $host['hostid']]
|
|
];
|
|
}
|
|
else {
|
|
unset($db_hosts[$host['hostid']]);
|
|
}
|
|
}
|
|
|
|
foreach ($db_hosts as $db_host) {
|
|
if (!array_key_exists($db_host['hostid'], $upd_hosts)) {
|
|
$upd_hosts[$db_host['hostid']] = [
|
|
'values' => ['proxyid' => 0],
|
|
'where' => ['hostid' => $db_host['hostid']]
|
|
];
|
|
}
|
|
}
|
|
}
|
|
unset($proxy);
|
|
|
|
if ($upd_hosts) {
|
|
DB::update('hosts', array_values($upd_hosts));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $proxyids
|
|
*
|
|
* @return array
|
|
*/
|
|
public function delete(array $proxyids): array {
|
|
$this->validateDelete($proxyids, $db_proxies);
|
|
|
|
DB::delete('proxy_rtdata', ['proxyid' => $proxyids]);
|
|
DB::delete('proxy', ['proxyid' => $proxyids]);
|
|
|
|
self::addAuditLog(CAudit::ACTION_DELETE, CAudit::RESOURCE_PROXY, $db_proxies);
|
|
|
|
return ['proxyids' => $proxyids];
|
|
}
|
|
|
|
/**
|
|
* @param array $proxyids
|
|
* @param array|null $db_proxies
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function validateDelete(array &$proxyids, ?array &$db_proxies): void {
|
|
if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS,
|
|
_s('No permissions to call "%1$s.%2$s".', 'proxy', __FUNCTION__)
|
|
);
|
|
}
|
|
|
|
$api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $proxyids, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$db_proxies = $this->get([
|
|
'output' => ['proxyid', 'name'],
|
|
'proxyids' => $proxyids,
|
|
'editable' => true,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
if (count($proxyids) != count($db_proxies)) {
|
|
self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
self::checkUsedInDiscovery($db_proxies);
|
|
self::checkUsedInHosts($db_proxies);
|
|
self::checkUsedInActions($db_proxies);
|
|
}
|
|
|
|
/**
|
|
* Check if proxy is used in network discovery rule.
|
|
*
|
|
* @param array $proxies
|
|
* @param string $proxies[<proxyid>]['host']
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkUsedInDiscovery(array $proxies): void {
|
|
$db_drules = DB::select('drules', [
|
|
'output' => ['proxyid', 'name'],
|
|
'filter' => ['proxyid' => array_keys($proxies)],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($db_drules) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Proxy "%1$s" is used by discovery rule "%2$s".',
|
|
$proxies[$db_drules[0]['proxyid']]['name'], $db_drules[0]['name']
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if proxy is used to monitor hosts.
|
|
*
|
|
* @param array $proxies
|
|
* @param string $proxies[<proxyid>]['host']
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkUsedInHosts(array $proxies): void {
|
|
$db_hosts = DB::select('hosts', [
|
|
'output' => ['proxyid', 'name'],
|
|
'filter' => ['proxyid' => array_keys($proxies)],
|
|
'limit' => 1
|
|
]);
|
|
|
|
if ($db_hosts) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Host "%1$s" is monitored by proxy "%2$s".',
|
|
$db_hosts[0]['name'], $proxies[$db_hosts[0]['proxyid']]['name']
|
|
));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if proxy is used in actions.
|
|
*
|
|
* @param array $proxies
|
|
* @param string $proxies[<proxyid>]['host']
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkUsedInActions(array $proxies): void {
|
|
$db_actions = DBfetchArray(DBselect(
|
|
'SELECT a.name,c.value AS proxyid'.
|
|
' FROM actions a,conditions c'.
|
|
' WHERE a.actionid=c.actionid'.
|
|
' AND c.conditiontype='.CONDITION_TYPE_PROXY.
|
|
' AND '.dbConditionString('c.value', array_keys($proxies)),
|
|
1
|
|
));
|
|
|
|
if ($db_actions) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Proxy "%1$s" is used by action "%2$s".',
|
|
$proxies[$db_actions[0]['proxyid']]['name'], $db_actions[0]['name']
|
|
));
|
|
}
|
|
}
|
|
|
|
protected function applyQueryOutputOptions($tableName, $tableAlias, array $options, array $sqlParts) {
|
|
$sqlParts = parent::applyQueryOutputOptions($tableName, $tableAlias, $options, $sqlParts);
|
|
|
|
if (!$options['countOutput']) {
|
|
$proxy_rtdata = false;
|
|
|
|
foreach (['lastaccess', 'version', 'compatibility'] as $field) {
|
|
if ($this->outputIsRequested($field, $options['output'])) {
|
|
$sqlParts = $this->addQuerySelect('pr.'.$field, $sqlParts);
|
|
$proxy_rtdata = true;
|
|
}
|
|
|
|
if (is_array($options['filter']) && array_key_exists($field, $options['filter'])) {
|
|
$proxy_rtdata = true;
|
|
}
|
|
}
|
|
|
|
if ($proxy_rtdata) {
|
|
$sqlParts['left_join'][] = ['alias' => 'pr', 'table' => 'proxy_rtdata', 'using' => 'proxyid'];
|
|
$sqlParts['left_table'] = ['alias' => $this->tableAlias, 'table' => $this->tableName];
|
|
}
|
|
}
|
|
|
|
return $sqlParts;
|
|
}
|
|
|
|
protected function addRelatedObjects(array $options, array $result) {
|
|
$result = parent::addRelatedObjects($options, $result);
|
|
|
|
if ($options['selectHosts'] !== null && $options['selectHosts'] != API_OUTPUT_COUNT) {
|
|
$output = $options['selectHosts'] === API_OUTPUT_EXTEND
|
|
? array_diff(CHost::OUTPUT_FIELDS, ['proxyid'])
|
|
: $options['selectHosts'];
|
|
|
|
$hosts = API::Host()->get([
|
|
'output' => $this->outputExtend($output, ['hostid', 'proxyid']),
|
|
'proxyids' => array_keys($result),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$relationMap = $this->createRelationMap($hosts, 'proxyid', 'hostid');
|
|
$hosts = $this->unsetExtraFields($hosts, ['proxyid', 'hostid'], $output);
|
|
$result = $relationMap->mapMany($result, $hosts, 'hosts');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function validateCreate(array &$proxies): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['name']], 'fields' => [
|
|
'name' => ['type' => API_H_NAME, 'flags' => API_REQUIRED, 'length' => DB::getFieldLength('proxy', 'name')],
|
|
'operating_mode' => ['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', [PROXY_OPERATING_MODE_ACTIVE, PROXY_OPERATING_MODE_PASSIVE])],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'description')],
|
|
'allowed_addresses' => ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS, 'length' => DB::getFieldLength('proxy', 'allowed_addresses')],
|
|
'address' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'address')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_HOST_ADDRESS, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'length' => DB::getFieldLength('proxy', 'address')]
|
|
]],
|
|
'port' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'port')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_PORT, 'flags' => API_REQUIRED | API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'length' => DB::getFieldLength('proxy', 'port')]
|
|
]],
|
|
'tls_connect' => ['type' => API_MULTIPLE, 'default' => HOST_ENCRYPTION_NONE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_INT32, 'in' => DB::getDefault('proxy', 'tls_connect')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_INT32, 'in' => implode(',', [HOST_ENCRYPTION_NONE, HOST_ENCRYPTION_PSK, HOST_ENCRYPTION_CERTIFICATE])]
|
|
]],
|
|
'tls_accept' => ['type' => API_MULTIPLE, 'default' => HOST_ENCRYPTION_NONE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_INT32, 'in' => HOST_ENCRYPTION_NONE.':'.(HOST_ENCRYPTION_NONE | HOST_ENCRYPTION_PSK | HOST_ENCRYPTION_CERTIFICATE)],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_INT32, 'in' => DB::getDefault('proxy', 'tls_accept')]
|
|
]],
|
|
'tls_issuer' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE; }, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_issuer')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_issuer')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_CERTIFICATE], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_issuer')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_issuer')]
|
|
]]
|
|
]],
|
|
'tls_subject' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE; }, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_subject')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_subject')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_CERTIFICATE], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_subject')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_subject')]
|
|
]]
|
|
]],
|
|
'tls_psk_identity' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_PSK; }, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk_identity')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk_identity')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_PSK], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk_identity')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk_identity')]
|
|
]]
|
|
]],
|
|
'tls_psk' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_PSK; }, 'type' => API_PSK, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_PSK], 'type' => API_PSK, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk')]
|
|
]]
|
|
]],
|
|
'hosts' => ['type' => API_OBJECTS, 'uniq' => [['hostid']], 'fields' => [
|
|
'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $proxies, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
self::checkDuplicates($proxies);
|
|
self::checkHosts($proxies);
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
* @param array|null $db_proxies
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkDuplicates(array $proxies, array $db_proxies = null): void {
|
|
$names = [];
|
|
|
|
foreach ($proxies as $proxy) {
|
|
if (!array_key_exists('name', $proxy)) {
|
|
continue;
|
|
}
|
|
|
|
if ($db_proxies === null || $proxy['name'] !== $db_proxies[$proxy['proxyid']]['name']) {
|
|
$names[] = $proxy['name'];
|
|
}
|
|
}
|
|
|
|
if (!$names) {
|
|
return;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['name'],
|
|
'filter' => [
|
|
'name' => $names
|
|
]
|
|
];
|
|
$duplicate = DBfetch(DBselect(DB::makeSql('proxy', $options), 1));
|
|
|
|
if ($duplicate) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _s('Proxy "%1$s" already exists.', $duplicate['name']));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
* @param array|null $db_proxies
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private static function checkHosts(array $proxies, array $db_proxies = null): void {
|
|
$_hostids = [];
|
|
|
|
foreach ($proxies as $proxy) {
|
|
if (!array_key_exists('hosts', $proxy)) {
|
|
continue;
|
|
}
|
|
|
|
$hostids = array_column($proxy['hosts'], null, 'hostid');
|
|
$db_hostids = $db_proxies !== null
|
|
? array_column($db_proxies[$proxy['proxyid']]['hosts'], null, 'hostid')
|
|
: [];
|
|
|
|
$_hostids += array_diff_key($hostids, $db_hostids);
|
|
}
|
|
|
|
if (!$_hostids) {
|
|
return;
|
|
}
|
|
|
|
$db_hosts = API::Host()->get([
|
|
'output' => ['hostid', 'host', 'flags'],
|
|
'hostids' => array_keys($_hostids),
|
|
'editable' => true
|
|
]);
|
|
|
|
if (count($db_hosts) != count($_hostids)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
foreach ($db_hosts as $db_host) {
|
|
if ($db_host['flags'] == ZBX_FLAG_DISCOVERY_CREATED) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS,
|
|
_s('Cannot update proxy for discovered host "%1$s".', $db_host['host'])
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
* @param array|null $db_proxies
|
|
*
|
|
* @throws APIException
|
|
*/
|
|
private function validateUpdate(array &$proxies, ?array &$db_proxies): void {
|
|
$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE | API_ALLOW_UNEXPECTED, 'uniq' => [['proxyid']], 'fields' => [
|
|
'proxyid' => ['type' => API_ID, 'flags' => API_REQUIRED],
|
|
'operating_mode' => ['type' => API_INT32, 'in' => implode(',', [PROXY_OPERATING_MODE_ACTIVE, PROXY_OPERATING_MODE_PASSIVE])]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $proxies, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
$count = $this->get([
|
|
'countOutput' => true,
|
|
'proxyids' => array_column($proxies, 'proxyid'),
|
|
'editable' => true
|
|
]);
|
|
|
|
if ($count != count($proxies)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
|
|
}
|
|
|
|
$db_proxies = DB::select('proxy', [
|
|
'output' => ['proxyid', 'name', 'operating_mode', 'description', 'allowed_addresses', 'address', 'port',
|
|
'tls_connect', 'tls_accept', 'tls_issuer', 'tls_subject', 'tls_psk_identity', 'tls_psk'
|
|
],
|
|
'proxyids' => array_column($proxies, 'proxyid'),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$proxies = $this->extendObjectsByKey($proxies, $db_proxies, 'proxyid', ['operating_mode']);
|
|
|
|
self::addFieldDefaultsByMode($proxies, $db_proxies);
|
|
|
|
$proxies = $this->extendObjectsByKey($proxies, $db_proxies, 'proxyid', ['tls_connect', 'tls_accept']);
|
|
|
|
$api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['name']], 'fields' => [
|
|
'proxyid' => ['type' => API_ANY],
|
|
'name' => ['type' => API_H_NAME, 'length' => DB::getFieldLength('proxy', 'name')],
|
|
'operating_mode' => ['type' => API_ANY],
|
|
'description' => ['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'description')],
|
|
'allowed_addresses' => ['type' => API_IP_RANGES, 'flags' => API_ALLOW_DNS, 'length' => DB::getFieldLength('proxy', 'allowed_addresses')],
|
|
'address' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'address')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_HOST_ADDRESS, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'length' => DB::getFieldLength('proxy', 'address')]
|
|
]],
|
|
'port' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'port')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_PORT, 'flags' => API_NOT_EMPTY | API_ALLOW_USER_MACRO, 'length' => DB::getFieldLength('proxy', 'port')]
|
|
]],
|
|
'tls_connect' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_INT32, 'in' => DB::getDefault('proxy', 'tls_connect')],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_INT32, 'in' => implode(',', [HOST_ENCRYPTION_NONE, HOST_ENCRYPTION_PSK, HOST_ENCRYPTION_CERTIFICATE])]
|
|
]],
|
|
'tls_accept' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_INT32, 'in' => HOST_ENCRYPTION_NONE.':'.(HOST_ENCRYPTION_NONE | HOST_ENCRYPTION_PSK | HOST_ENCRYPTION_CERTIFICATE)],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_INT32, 'in' => DB::getDefault('proxy', 'tls_accept')]
|
|
]],
|
|
'tls_issuer' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE; }, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_issuer')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_issuer')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_CERTIFICATE], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_issuer')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_issuer')]
|
|
]]
|
|
]],
|
|
'tls_subject' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE; }, 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_subject')],
|
|
['else' => true, 'type' => API_STRING_UTF8, DB::getDefault('proxy', 'tls_subject')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_CERTIFICATE], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('proxy', 'tls_subject')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_subject')]
|
|
]]
|
|
]],
|
|
'tls_psk_identity' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_PSK; }, 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk_identity')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk_identity')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_PSK], 'type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk_identity')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk_identity')]
|
|
]]
|
|
]],
|
|
'tls_psk' => ['type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_ACTIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => static function ($data) { return $data['tls_accept'] & HOST_ENCRYPTION_PSK; }, 'type' => API_PSK, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk')]
|
|
]],
|
|
['if' => ['field' => 'operating_mode', 'in' => PROXY_OPERATING_MODE_PASSIVE], 'type' => API_MULTIPLE, 'rules' => [
|
|
['if' => ['field' => 'tls_connect', 'in' => HOST_ENCRYPTION_PSK], 'type' => API_PSK, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('proxy', 'tls_psk')],
|
|
['else' => true, 'type' => API_STRING_UTF8, 'in' => DB::getDefault('proxy', 'tls_psk')]
|
|
]]
|
|
]],
|
|
'hosts' => ['type' => API_OBJECTS, 'uniq' => [['hostid']], 'fields' => [
|
|
'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED]
|
|
]]
|
|
]];
|
|
|
|
if (!CApiInputValidator::validate($api_input_rules, $proxies, '/', $error)) {
|
|
self::exception(ZBX_API_ERROR_PARAMETERS, $error);
|
|
}
|
|
|
|
self::addAffectedObjects($proxies, $db_proxies);
|
|
self::checkDuplicates($proxies, $db_proxies);
|
|
self::checkHosts($proxies, $db_proxies);
|
|
}
|
|
|
|
/**
|
|
* Add default values for fields that became unnecessary as the result of the change of operating_mode.
|
|
*
|
|
* @param array $proxies
|
|
* @param array $db_proxies
|
|
*/
|
|
private static function addFieldDefaultsByMode(array &$proxies, array $db_proxies): void {
|
|
foreach ($proxies as &$proxy) {
|
|
if ($proxy['operating_mode'] != $db_proxies[$proxy['proxyid']]['operating_mode']) {
|
|
$proxy += $proxy['operating_mode'] == PROXY_OPERATING_MODE_ACTIVE
|
|
? ['tls_connect' => DB::getDefault('proxy', 'tls_connect')]
|
|
: ['tls_accept' => DB::getDefault('proxy', 'tls_accept')];
|
|
}
|
|
}
|
|
unset($proxy);
|
|
}
|
|
|
|
/**
|
|
* Add default values for fields that became unnecessary as the result of the change of operating_mode and
|
|
* TLS fields.
|
|
*
|
|
* @param array $proxies
|
|
* @param array $db_proxies
|
|
*/
|
|
protected static function addFieldDefaultsByTls(array &$proxies, array $db_proxies): void {
|
|
foreach ($proxies as &$proxy) {
|
|
$db_proxy = $db_proxies[$proxy['proxyid']];
|
|
|
|
if ($proxy['operating_mode'] != $db_proxy['operating_mode'] || $proxy['tls_connect'] != $db_proxy['tls_connect']
|
|
|| $proxy['tls_accept'] != $db_proxy['tls_accept']) {
|
|
if (($proxy['operating_mode'] == PROXY_OPERATING_MODE_ACTIVE && ($proxy['tls_accept'] & HOST_ENCRYPTION_CERTIFICATE) == 0)
|
|
|| ($proxy['operating_mode'] == PROXY_OPERATING_MODE_PASSIVE
|
|
&& $proxy['tls_connect'] != HOST_ENCRYPTION_CERTIFICATE)) {
|
|
$proxy += [
|
|
'tls_issuer' => DB::getDefault('proxy', 'tls_issuer'),
|
|
'tls_subject' => DB::getDefault('proxy', 'tls_subject')
|
|
];
|
|
}
|
|
|
|
if (($proxy['operating_mode'] == PROXY_OPERATING_MODE_ACTIVE && ($proxy['tls_accept'] & HOST_ENCRYPTION_PSK) == 0)
|
|
|| ($proxy['operating_mode'] == PROXY_OPERATING_MODE_PASSIVE && $proxy['tls_connect'] != HOST_ENCRYPTION_PSK)) {
|
|
$proxy += DB::getUpdatedValues('proxy', [
|
|
'tls_psk_identity' => DB::getDefault('proxy', 'tls_psk_identity'),
|
|
'tls_psk' => DB::getDefault('proxy', 'tls_psk')
|
|
], $db_proxy);
|
|
}
|
|
}
|
|
}
|
|
unset($proxy);
|
|
}
|
|
|
|
/**
|
|
* @param array $proxies
|
|
* @param array $db_proxies
|
|
*/
|
|
private static function addAffectedObjects(array $proxies, array &$db_proxies): void {
|
|
$proxyids = ['hosts' => []];
|
|
|
|
foreach ($proxies as $proxy) {
|
|
if (array_key_exists('hosts', $proxy)) {
|
|
$proxyids['hosts'][] = $proxy['proxyid'];
|
|
$db_proxies[$proxy['proxyid']]['hosts'] = [];
|
|
}
|
|
}
|
|
|
|
if ($proxyids['hosts']) {
|
|
$options = [
|
|
'output' => ['hostid', 'proxyid'],
|
|
'filter' => ['proxyid' => $proxyids['hosts']]
|
|
];
|
|
$db_hosts = DBselect(DB::makeSql('hosts', $options));
|
|
|
|
while ($db_host = DBfetch($db_hosts)) {
|
|
$db_proxies[$db_host['proxyid']]['hosts'][$db_host['hostid']] = [
|
|
'hostid' => $db_host['hostid']
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|