Skip to content

Commit

Permalink
Apply custom variable restrictions when necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
nilmerg committed Mar 12, 2021
1 parent 989d6fc commit 38de00b
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 24 deletions.
127 changes: 112 additions & 15 deletions library/Icingadb/Common/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use ipl\Orm\Compat\FilterProcessor;
use ipl\Orm\Query;
use ipl\Orm\UnionQuery;
use ipl\Sql\Expression;
use ipl\Sql\Filter\IsNull;
use ipl\Stdlib\Filter;
use ipl\Web\Filter\QueryString;
Expand All @@ -24,7 +25,8 @@ public function getAuth()
*
* This will apply `icingadb/filter/objects` in any case. `icingadb/filter/services` is only
* applied to queries fetching services and `icingadb/filter/hosts` is applied to queries
* fetching either hosts or services.
* fetching either hosts or services. It also applies custom variable restrictions and
* obfuscations. (`icingadb/blacklist/variables` and `icingadb/protect/variables`)
*
* @param Query $query
*
Expand All @@ -44,38 +46,115 @@ public function applyRestrictions(Query $query)

foreach ($queries as $query) {
$relations = [$query->getModel()->getTableName()];
foreach ($query->getWith() as $relation) {
$relations[] = $relation->getTarget()->getTableName();
foreach ($query->getWith() as $relationPath => $relation) {
$relations[$relationPath] = $relation->getTarget()->getTableName();
}

$customVarRelationName = array_search('customvar_flat', $relations, true);
$applyServiceRestriction = in_array('service', $relations, true);
$applyHostRestriction = in_array('host', $relations, true)
// Hosts and services have a special relation as a service can't exist without its host.
// Hence why the hosts restriction is also applied if only services are queried.
|| $applyServiceRestriction;

$resolver = $query->getResolver();

$queryFilter = Filter::any();
$obfuscationRules = Filter::any();
foreach ($this->getAuth()->getUser()->getRoles() as $role) {
$roleFilter = Filter::all();

if (($restriction = $role->getRestrictions('icingadb/filter/objects'))) {
$roleFilter->add($this->parseRestriction($restriction, 'icingadb/filter/objects'));
if ($customVarRelationName !== false) {
if (($restriction = $role->getRestrictions('icingadb/blacklist/variables'))) {
$roleFilter->add($this->parseBlacklist(
$restriction,
$customVarRelationName
? $resolver->qualifyColumn('flatname', $customVarRelationName)
: 'flatname'
));
}

if (($restriction = $role->getRestrictions('icingadb/protect/variables'))) {
$obfuscationRules->add($this->parseBlacklist(
$restriction,
$customVarRelationName
? $resolver->qualifyColumn('flatname', $customVarRelationName)
: 'flatname'
));
}
}

if ($customVarRelationName === false || count($relations) > 1) {
if (($restriction = $role->getRestrictions('icingadb/filter/objects'))) {
$roleFilter->add($this->parseRestriction($restriction, 'icingadb/filter/objects'));
}

if ($applyHostRestriction && ($restriction = $role->getRestrictions('icingadb/filter/hosts'))) {
$roleFilter->add($this->parseRestriction($restriction, 'icingadb/filter/hosts'));
}

if (
$applyServiceRestriction
&& ($restriction = $role->getRestrictions('icingadb/filter/services'))
) {
$roleFilter->add(
Filter::any(
new IsNull('service.id'),
$this->parseRestriction($restriction, 'icingadb/filter/services')
)
);
}
}

if ($applyHostRestriction && ($restriction = $role->getRestrictions('icingadb/filter/hosts'))) {
$roleFilter->add($this->parseRestriction($restriction, 'icingadb/filter/hosts'));
if (! $roleFilter->isEmpty()) {
$queryFilter->add($roleFilter);
}
}

if ($applyServiceRestriction && ($restriction = $role->getRestrictions('icingadb/filter/services'))) {
$roleFilter->add(
Filter::any(
new IsNull('service.id'),
$this->parseRestriction($restriction, 'icingadb/filter/services')
)
);
if (! $obfuscationRules->isEmpty()) {
$flatvaluePath = $customVarRelationName
? $resolver->qualifyColumn('flatvalue', $customVarRelationName)
: 'flatvalue';

$columns = $query->getColumns();
if (empty($columns)) {
$columns = [$flatvaluePath, '*'];
}

$queryFilter->add($roleFilter);
$flatvalue = null;
if (isset($columns[$flatvaluePath])) {
$flatvalue = $columns[$flatvaluePath];
} else {
$flatvaluePathAt = array_search($flatvaluePath, $columns, true);
if ($flatvaluePathAt !== false) {
$flatvalue = $columns[$flatvaluePathAt];
if (is_int($flatvaluePathAt)) {
unset($columns[$flatvaluePathAt]);
} else {
$flatvaluePath = $flatvaluePathAt;
}
}
}

if ($flatvalue !== null) {
// TODO: The four lines below are needed because there is still no way to postpone filter column
// qualification. (i.e. Just like the expression, filter rules need to be handled the same
// so that their columns are qualified lazily when assembling the query)
$queryClone = clone $query;
$queryClone->getSelectBase()->resetWhere();
FilterProcessor::apply($obfuscationRules, $queryClone);
$where = $queryClone->getSelectBase()->getWhere();

$values = [];
$rendered = $this->getDb()->getQueryBuilder()->buildCondition($where, $values);
$columns[$flatvaluePath] = new Expression(
"CASE WHEN (" . $rendered . ") THEN (%s) ELSE '***' END",
[$flatvalue],
...$values
);

$query->setColumns($columns);
}
}

FilterProcessor::apply($queryFilter, $query);
Expand Down Expand Up @@ -141,4 +220,22 @@ function ($k, $v) {
}
)->parse();
}

/**
* Parse the given blacklist
*
* @param string $blacklist Comma separated list of column names
* @param string $column The column which should not equal any of the blacklisted names
*
* @return Filter\None
*/
protected function parseBlacklist($blacklist, $column)
{
$filter = Filter::none();
foreach (explode(',', $blacklist) as $value) {
$filter->add(Filter::equal($column, trim($value)));
}

return $filter;
}
}
16 changes: 10 additions & 6 deletions library/Icingadb/Web/Control/SearchBar/ObjectSuggestions.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,30 +235,34 @@ protected function queryCustomvarConfig($searchTerm)
{
$customVars = CustomvarFlat::on($this->getDb());
$tableName = $customVars->getModel()->getTableName();
$resolver = $customVars->getResolver();

$columns = ['flatname'];
$scalarQueries = [];
$aggregates = ['flatname'];
foreach ($customVars->getResolver()->getRelations($customVars->getModel()) as $name => $relation) {
foreach ($resolver->getRelations($customVars->getModel()) as $name => $relation) {
if (isset($this->customVarSources[$name]) && $relation instanceof BelongsToMany) {
$query = $customVars->createSubQuery(
$relation->getTarget(),
$customVars->getResolver()->qualifyPath($name, $tableName)
$resolver->qualifyPath($name, $tableName)
);

$this->applyRestrictions($query);

$aggregates[$name] = new Expression("MAX($name)");
$columns[$name] = $query->assembleSelect()
$scalarQueries[$name] = $query->assembleSelect()
->resetColumns()->columns(new Expression('1'))
->limit(1);
}
}

$customVars->columns('flatname');
$this->applyRestrictions($customVars);
FilterProcessor::apply(Filter::equal('flatname', $searchTerm), $customVars);
$idColumn = $resolver->qualifyColumnsAndAliases((array) 'id', $customVars->getModel(), false);
$customVars = $customVars->assembleSelect();

$customVars->resetColumns()->columns($columns);
$customVars->groupBy('id');
$customVars->columns($scalarQueries);
$customVars->groupBy($idColumn);
$customVars->limit(static::DEFAULT_LIMIT);

// This outer query exists only because there's no way to combine aggregates and sub queries (yet)
Expand Down
6 changes: 3 additions & 3 deletions library/Icingadb/Widget/Detail/ObjectDetail.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ protected function createComments()
protected function createCustomVars()
{
$content = [Html::tag('h2', t('Custom Variables'))];
$vars = $this->object->customvar_flat->getModel()->unflattenVars(
$this->object->customvar_flat
);
$flattenedVars = $this->object->customvar_flat;
$this->applyRestrictions($flattenedVars);

$vars = $this->object->customvar_flat->getModel()->unflattenVars($flattenedVars);
if (! empty($vars)) {
$customvarTable = new CustomVarTable($vars);
$customvarTable->setAttribute('id', $this->objectType . '-customvars');
Expand Down

0 comments on commit 38de00b

Please sign in to comment.