parent
66a7bdfc84
commit
589da9bcd1
|
@ -0,0 +1,180 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Icinga\Util;
|
||||||
|
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GLOB-like filter for simple data structures
|
||||||
|
*
|
||||||
|
* e.g. this filters:
|
||||||
|
*
|
||||||
|
* foo.bar.baz
|
||||||
|
* foo.b*r.baz
|
||||||
|
* **.baz
|
||||||
|
*
|
||||||
|
* match this one:
|
||||||
|
*
|
||||||
|
* array(
|
||||||
|
* 'foo' => array(
|
||||||
|
* 'bar' => array(
|
||||||
|
* 'baz' => 'deadbeef' // <---
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
* )
|
||||||
|
*/
|
||||||
|
class GlobFilter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The prepared filters
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $filters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new filter from a comma-separated list of GLOB-like filters or an array of such lists.
|
||||||
|
*
|
||||||
|
* @param string|\Traversable $filters
|
||||||
|
*/
|
||||||
|
public function __construct($filters)
|
||||||
|
{
|
||||||
|
$patterns = array(array(''));
|
||||||
|
$lastIndex1 = $lastIndex2 = 0;
|
||||||
|
|
||||||
|
foreach ((is_string($filters) ? array($filters) : $filters) as $rawPatterns) {
|
||||||
|
$escape = false;
|
||||||
|
|
||||||
|
foreach (str_split($rawPatterns) as $c) {
|
||||||
|
if ($escape) {
|
||||||
|
$escape = false;
|
||||||
|
$patterns[$lastIndex1][$lastIndex2] .= preg_quote($c, '/');
|
||||||
|
} else {
|
||||||
|
switch ($c) {
|
||||||
|
case '\\':
|
||||||
|
$escape = true;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
$patterns[] = array('');
|
||||||
|
++$lastIndex1;
|
||||||
|
$lastIndex2 = 0;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
$patterns[$lastIndex1][] = '';
|
||||||
|
++$lastIndex2;
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
$patterns[$lastIndex1][$lastIndex2] .= '.*';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$patterns[$lastIndex1][$lastIndex2] .= preg_quote($c, '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($escape) {
|
||||||
|
$patterns[$lastIndex1][$lastIndex2] .= '\\\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->filters = array();
|
||||||
|
|
||||||
|
foreach ($patterns as $pattern) {
|
||||||
|
foreach ($pattern as $i => $subPattern) {
|
||||||
|
if ($subPattern === '') {
|
||||||
|
unset($pattern[$i]);
|
||||||
|
} elseif ($subPattern === '.*.*') {
|
||||||
|
$pattern[$i] = '**';
|
||||||
|
} else {
|
||||||
|
$pattern[$i] = '/^' . $subPattern . '$/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($pattern)) {
|
||||||
|
$found = false;
|
||||||
|
foreach ($pattern as $i => $v) {
|
||||||
|
if ($found) {
|
||||||
|
if ($v === '**') {
|
||||||
|
unset($pattern[$i]);
|
||||||
|
} else {
|
||||||
|
$found = false;
|
||||||
|
}
|
||||||
|
} elseif ($v === '**') {
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end($pattern) === '**') {
|
||||||
|
$pattern[] = '/^.*$/';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->filters[] = array_values($pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all keys/attributes matching any of $this->filters from $dataStructure
|
||||||
|
*
|
||||||
|
* @param stdClass|array $dataStructure
|
||||||
|
*
|
||||||
|
* @return stdClass|array The modified copy of $dataStructure
|
||||||
|
*/
|
||||||
|
public function removeMatching($dataStructure)
|
||||||
|
{
|
||||||
|
foreach ($this->filters as $filter) {
|
||||||
|
$dataStructure = static::removeMatchingRecursive($dataStructure, $filter);
|
||||||
|
}
|
||||||
|
return $dataStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method for removeMatching()
|
||||||
|
*
|
||||||
|
* @param stdClass|array $dataStructure
|
||||||
|
* @param array $filter
|
||||||
|
*
|
||||||
|
* @return stdClass|array
|
||||||
|
*/
|
||||||
|
protected static function removeMatchingRecursive($dataStructure, $filter)
|
||||||
|
{
|
||||||
|
$multiLevelPattern = $filter[0] === '**';
|
||||||
|
if ($multiLevelPattern) {
|
||||||
|
$dataStructure = static::removeMatchingRecursive($dataStructure, array_slice($filter, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
$isObject = $dataStructure instanceof stdClass;
|
||||||
|
if ($isObject || is_array($dataStructure)) {
|
||||||
|
if ($isObject) {
|
||||||
|
$dataStructure = (array) $dataStructure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($multiLevelPattern) {
|
||||||
|
foreach ($dataStructure as $k => & $v) {
|
||||||
|
$v = static::removeMatchingRecursive($v, $filter);
|
||||||
|
unset($v);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$currentLevel = $filter[0];
|
||||||
|
$nextLevels = count($filter) === 1 ? null : array_slice($filter, 1);
|
||||||
|
foreach ($dataStructure as $k => & $v) {
|
||||||
|
if (preg_match($currentLevel, (string) $k)) {
|
||||||
|
if ($nextLevels === null) {
|
||||||
|
unset($dataStructure[$k]);
|
||||||
|
} else {
|
||||||
|
$v = static::removeMatchingRecursive($v, $nextLevels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isObject) {
|
||||||
|
$dataStructure = (object) $dataStructure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dataStructure;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ use Icinga\Data\Filterable;
|
||||||
use Icinga\Exception\InvalidPropertyException;
|
use Icinga\Exception\InvalidPropertyException;
|
||||||
use Icinga\Exception\ProgrammingError;
|
use Icinga\Exception\ProgrammingError;
|
||||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||||
|
use Icinga\Util\GlobFilter;
|
||||||
use Icinga\Web\UrlParams;
|
use Icinga\Web\UrlParams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -151,7 +152,7 @@ abstract class MonitoredObject implements Filterable
|
||||||
/**
|
/**
|
||||||
* The properties to hide from the user
|
* The properties to hide from the user
|
||||||
*
|
*
|
||||||
* @var array
|
* @var GlobFilter
|
||||||
*/
|
*/
|
||||||
protected $blacklistedProperties = null;
|
protected $blacklistedProperties = null;
|
||||||
|
|
||||||
|
@ -503,69 +504,15 @@ abstract class MonitoredObject implements Filterable
|
||||||
protected function hideBlacklistedProperties()
|
protected function hideBlacklistedProperties()
|
||||||
{
|
{
|
||||||
if ($this->blacklistedProperties === null) {
|
if ($this->blacklistedProperties === null) {
|
||||||
$this->blacklistedProperties = array();
|
$this->blacklistedProperties = new GlobFilter(
|
||||||
foreach (Auth::getInstance()->getRestrictions('monitoring/blacklist/properties') as $patterns) {
|
Auth::getInstance()->getRestrictions('monitoring/blacklist/properties')
|
||||||
foreach (explode(',', $patterns) as $pattern) {
|
);
|
||||||
$pattern = explode('.', $pattern);
|
|
||||||
foreach ($pattern as & $subPattern) {
|
|
||||||
$subPattern = explode('*', $subPattern);
|
|
||||||
foreach ($subPattern as & $subPatternPart) {
|
|
||||||
if ($subPatternPart !== '') {
|
|
||||||
$subPatternPart = preg_quote($subPatternPart, '/');
|
|
||||||
}
|
|
||||||
unset($subPatternPart);
|
|
||||||
}
|
|
||||||
$subPattern = '/^' . implode('.*', $subPattern) . '$/';
|
|
||||||
unset($subPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->blacklistedProperties[] = $pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$allProperties = array($this->type => array('vars' => $this->customvars));
|
$allProperties = $this->blacklistedProperties->removeMatching(
|
||||||
foreach ($this->blacklistedProperties as $blacklistedProperty) {
|
array($this->type => array('vars' => $this->customvars))
|
||||||
$allProperties = $this->hideBlacklistedPropertiesRecursive($allProperties, $blacklistedProperty);
|
);
|
||||||
}
|
$this->customvars = isset($allProperties[$this->type]['vars']) ? $allProperties[$this->type]['vars'] : array();
|
||||||
$this->customvars = $allProperties[$this->type]['vars'];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method for hideBlacklistedProperties()
|
|
||||||
*
|
|
||||||
* @param stdClass|array $allProperties
|
|
||||||
* @param array $blacklistedProperty
|
|
||||||
*
|
|
||||||
* @return stdClass|array
|
|
||||||
*/
|
|
||||||
protected function hideBlacklistedPropertiesRecursive($allProperties, $blacklistedProperty)
|
|
||||||
{
|
|
||||||
$isObject = $allProperties instanceof stdClass;
|
|
||||||
if ($isObject || is_array($allProperties)) {
|
|
||||||
if ($isObject) {
|
|
||||||
$allProperties = (array) $allProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
$currentLevel = $blacklistedProperty[0];
|
|
||||||
$nextLevels = count($blacklistedProperty) === 1 ? null : array_slice($blacklistedProperty, 1);
|
|
||||||
foreach ($allProperties as $k => & $v) {
|
|
||||||
if (preg_match($currentLevel, (string) $k)) {
|
|
||||||
if ($nextLevels === null) {
|
|
||||||
unset($allProperties[$k]);
|
|
||||||
} else {
|
|
||||||
$v = $this->hideBlacklistedPropertiesRecursive($v, $nextLevels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unset($v);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isObject) {
|
|
||||||
$allProperties = (object) $allProperties;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $allProperties;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,325 @@
|
||||||
|
<?php
|
||||||
|
/* Icinga Web 2 | (c) 2016 Icinga Development Team | GPLv2+ */
|
||||||
|
|
||||||
|
namespace Tests\Icinga\Util;
|
||||||
|
|
||||||
|
use Icinga\Util\GlobFilter;
|
||||||
|
use Icinga\Test\BaseTestCase;
|
||||||
|
|
||||||
|
class GlobFilterTest extends BaseTestCase
|
||||||
|
{
|
||||||
|
protected function assertGlobFilterRemovesMatching($filterPattern, $unfiltered, $filtered)
|
||||||
|
{
|
||||||
|
$filter = new GlobFilter($filterPattern);
|
||||||
|
$this->assertTrue(
|
||||||
|
$filter->removeMatching($unfiltered) === $filtered,
|
||||||
|
'Filter `' . $filterPattern . '\' doesn\'t work as intended'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithoutAnyWildcards()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.cmdb_name',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'cmdb_id' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_id' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithAnAsteriskAtTheEndOfAComponent()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.cmdb_*',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'cmdb_id' => '',
|
||||||
|
'cmdb_location' => '',
|
||||||
|
'wiki_id' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'wiki_id' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithAnAsteriskAtTheBeginningOfAComponent()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.*id',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'cmdb_id' => '',
|
||||||
|
'wiki_id' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'wiki_id' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'legacy' => array(
|
||||||
|
'wiki_id' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithAComponentBeingTheAsteriskOnly()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.*.mysql_password',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'mysql_password' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'ldap_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithTwoComponentsContainingAsterisks()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.*.*password',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => '',
|
||||||
|
'mongodb_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'mysql_password' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(),
|
||||||
|
'legacy' => array(
|
||||||
|
'cmdb_name' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTwoCommaSeparatedPatternsEachWithAnAsterisk()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.*.mysql_password,host.vars.*.ldap_password',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => '',
|
||||||
|
'mongodb_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'mysql_password' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_name' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mongodb_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithAComponentBeingTheMultiLevelWildcard()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.**.*password',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_location' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => '',
|
||||||
|
'mongodb_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'cmdb_location' => '',
|
||||||
|
'passwords' => array(),
|
||||||
|
'legacy' => array(),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPatternWithAnEscapedAsterisk()
|
||||||
|
{
|
||||||
|
$this->assertGlobFilterRemovesMatching(
|
||||||
|
'host.vars.**.\*password',
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'wiki_id' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mongodb_password' => '',
|
||||||
|
'*password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'mysql_password' => '',
|
||||||
|
'*password' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'*password' => '',
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'host' => array(
|
||||||
|
'vars' => array(
|
||||||
|
'wiki_id' => '',
|
||||||
|
'passwords' => array(
|
||||||
|
'mongodb_password' => ''
|
||||||
|
),
|
||||||
|
'legacy' => array(
|
||||||
|
'mysql_password' => ''
|
||||||
|
),
|
||||||
|
'backup' => array(
|
||||||
|
'passwords' => array(
|
||||||
|
'ldap_password' => ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue