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\ProgrammingError;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
use Icinga\Util\GlobFilter;
|
||||
use Icinga\Web\UrlParams;
|
||||
|
||||
/**
|
||||
|
@ -151,7 +152,7 @@ abstract class MonitoredObject implements Filterable
|
|||
/**
|
||||
* The properties to hide from the user
|
||||
*
|
||||
* @var array
|
||||
* @var GlobFilter
|
||||
*/
|
||||
protected $blacklistedProperties = null;
|
||||
|
||||
|
@ -503,69 +504,15 @@ abstract class MonitoredObject implements Filterable
|
|||
protected function hideBlacklistedProperties()
|
||||
{
|
||||
if ($this->blacklistedProperties === null) {
|
||||
$this->blacklistedProperties = array();
|
||||
foreach (Auth::getInstance()->getRestrictions('monitoring/blacklist/properties') as $patterns) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
$this->blacklistedProperties = new GlobFilter(
|
||||
Auth::getInstance()->getRestrictions('monitoring/blacklist/properties')
|
||||
);
|
||||
}
|
||||
|
||||
$allProperties = array($this->type => array('vars' => $this->customvars));
|
||||
foreach ($this->blacklistedProperties as $blacklistedProperty) {
|
||||
$allProperties = $this->hideBlacklistedPropertiesRecursive($allProperties, $blacklistedProperty);
|
||||
}
|
||||
$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;
|
||||
$allProperties = $this->blacklistedProperties->removeMatching(
|
||||
array($this->type => array('vars' => $this->customvars))
|
||||
);
|
||||
$this->customvars = isset($allProperties[$this->type]['vars']) ? $allProperties[$this->type]['vars'] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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