diff options
Diffstat (limited to '')
-rw-r--r-- | library/Director/Restriction/FilterByNameRestriction.php | 64 | ||||
-rw-r--r-- | library/Director/Restriction/HostgroupRestriction.php | 171 | ||||
-rw-r--r-- | library/Director/Restriction/MatchingFilter.php | 40 | ||||
-rw-r--r-- | library/Director/Restriction/ObjectRestriction.php | 84 |
4 files changed, 359 insertions, 0 deletions
diff --git a/library/Director/Restriction/FilterByNameRestriction.php b/library/Director/Restriction/FilterByNameRestriction.php new file mode 100644 index 0000000..8c3b256 --- /dev/null +++ b/library/Director/Restriction/FilterByNameRestriction.php @@ -0,0 +1,64 @@ +<?php + +namespace Icinga\Module\Director\Restriction; + +use gipfl\IcingaWeb2\Zf1\Db\FilterRenderer; +use Icinga\Authentication\Auth; +use Icinga\Data\Filter\Filter; +use Icinga\Module\Director\Db; +use Icinga\Module\Director\Objects\IcingaObject; +use Zend_Db_Select as ZfSelect; + +class FilterByNameRestriction extends ObjectRestriction +{ + protected $type; + + /** @var Filter */ + protected $filter; + + public function __construct(Db $connection, Auth $auth, $type) + { + parent::__construct($connection, $auth); + $this->setType($type); + } + + protected function setType($type) + { + $this->type = $type; + $this->setNameForType($type); + } + + protected function setNameForType($type) + { + $this->name = "director/${type}/filter-by-name"; + } + + public function allows(IcingaObject $object) + { + if (! $this->isRestricted()) { + return true; + } + + return $this->getFilter()->matches([ + (object) ['object_name' => $object->getObjectName()] + ]); + } + + public function getFilter() + { + if ($this->filter === null) { + $this->filter = MatchingFilter::forUser( + $this->auth->getUser(), + $this->name, + 'object_name' + ); + } + + return $this->filter; + } + + protected function filterQuery(ZfSelect $query, $tableAlias = 'o') + { + FilterRenderer::applyToQuery($this->getFilter(), $query); + } +} diff --git a/library/Director/Restriction/HostgroupRestriction.php b/library/Director/Restriction/HostgroupRestriction.php new file mode 100644 index 0000000..1a6792b --- /dev/null +++ b/library/Director/Restriction/HostgroupRestriction.php @@ -0,0 +1,171 @@ +<?php + +namespace Icinga\Module\Director\Restriction; + +use Icinga\Exception\ProgrammingError; +use Icinga\Module\Director\Db\IcingaObjectFilterHelper; +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Objects\IcingaHostGroup; +use Icinga\Module\Director\Objects\IcingaObject; +use Zend_Db_Select as ZfSelect; + +class HostgroupRestriction extends ObjectRestriction +{ + protected $name = 'director/filter/hostgroups'; + + public function allows(IcingaObject $object) + { + if ($object instanceof IcingaHost) { + return $this->allowsHost($object); + } elseif ($object instanceof IcingaHostGroup) { + return $this->allowsHostGroup($object); + } else { + return $this; + } + } + + protected function filterQuery(ZfSelect $query, $tableAlias = 'o') + { + $table = $this->getQueryTableByAlias($query, $tableAlias); + switch ($table) { + case 'icinga_host': + $this->filterHostsQuery($query, $tableAlias); + break; + case 'icinga_service': + // TODO: Alias is hardcoded + $this->filterHostsQuery($query, 'h'); + break; + case 'icinga_hostgroup': + $this->filterHostGroupsQuery($query, $tableAlias); + break; + // Hint: other tables are ignored, so please take care! + } + + return $query; + } + + /** + * Whether access to the given host is allowed + * + * @param IcingaHost $host + * @return bool + */ + public function allowsHost(IcingaHost $host) + { + if (! $this->isRestricted()) { + return true; + } + + // Hint: branched hosts have no id + if (! $host->hasBeenLoadedFromDb() || $host->hasModifiedGroups() || $host->get('id') === null) { + foreach ($this->listRestrictedHostgroups() as $group) { + if ($host->hasGroup($group)) { + return true; + } + } + + return false; + } + + $query = $this->db->select()->from( + ['o' => 'icinga_host'], + ['id'] + )->where('o.id = ?', $host->id); + + $this->filterHostsQuery($query); + return (int) $this->db->fetchOne($query) === (int) $host->get('id'); + } + + /** + * Whether access to the given hostgroup is allowed + * + * @param IcingaHostGroup $hostgroup + * @return bool + */ + public function allowsHostGroup(IcingaHostGroup $hostgroup) + { + if (! $this->isRestricted()) { + return true; + } + + $query = $this->db->select()->from( + ['h' => 'icinga_hostgroup'], + ['id'] + )->where('id = ?', $hostgroup->id); + + $this->filterHostGroupsQuery($query); + return (int) $this->db->fetchOne($query) === (int) $hostgroup->get('id'); + } + + /** + * Apply the restriction to the given Hosts Query + * + * We assume that the query wants to fetch hosts and that therefore the + * icinga_host table already exists in the given query, using the $tableAlias + * alias. + * + * @param ZfSelect $query + * @param string $tableAlias + */ + public function filterHostsQuery(ZfSelect $query, $tableAlias = 'o') + { + if (! $this->isRestricted()) { + return; + } + + IcingaObjectFilterHelper::filterByResolvedHostgroups( + $query, + 'host', + $this->listRestrictedHostgroups(), + $tableAlias + ); + } + + /** + * Apply the restriction to the given Hosts Query + * + * We assume that the query wants to fetch hosts and that therefore the + * icinga_host table already exists in the given query, using the $tableAlias + * alias. + * + * @param ZfSelect $query + * @param string $tableAlias + */ + protected function filterHostGroupsQuery(ZfSelect $query, $tableAlias = 'o') + { + if (! $this->isRestricted()) { + return; + } + $groups = $this->listRestrictedHostgroups(); + + if (empty($groups)) { + $query->where('(1 = 0)'); + } else { + $query->where("${tableAlias}.object_name IN (?)", $groups); + } + } + + /** + * Give a list of allowed Hostgroups + * + * When not restricted, null is returned. This might eventually also give + * an empty list, and therefore not allow any access at all + * + * @return array|null + */ + protected function listRestrictedHostgroups() + { + if ($restrictions = $this->auth->getRestrictions($this->getName())) { + $groups = array(); + foreach ($restrictions as $restriction) { + foreach ($this->gracefullySplitOnComma($restriction) as $group) { + $groups[$group] = $group; + } + } + + return array_keys($groups); + } else { + return null; + } + } +} diff --git a/library/Director/Restriction/MatchingFilter.php b/library/Director/Restriction/MatchingFilter.php new file mode 100644 index 0000000..162840c --- /dev/null +++ b/library/Director/Restriction/MatchingFilter.php @@ -0,0 +1,40 @@ +<?php + +namespace Icinga\Module\Director\Restriction; + +use Icinga\Data\Filter\Filter; +use Icinga\User; + +class MatchingFilter +{ + public static function forPatterns(array $restrictions, $columnName) + { + $filters = []; + foreach ($restrictions as $restriction) { + foreach (preg_split('/\|/', $restriction) as $pattern) { + $filters[] = Filter::expression( + $columnName, + '=', + $pattern + ); + } + } + + if (count($filters) === 1) { + return $filters[0]; + } else { + return Filter::matchAny($filters); + } + } + + public static function forUser( + User $user, + $restrictionName, + $columnName + ) { + return static::forPatterns( + $user->getRestrictions($restrictionName), + $columnName + ); + } +} diff --git a/library/Director/Restriction/ObjectRestriction.php b/library/Director/Restriction/ObjectRestriction.php new file mode 100644 index 0000000..9161ebb --- /dev/null +++ b/library/Director/Restriction/ObjectRestriction.php @@ -0,0 +1,84 @@ +<?php + +namespace Icinga\Module\Director\Restriction; + +use Icinga\Authentication\Auth; +use Icinga\Exception\ProgrammingError; +use Icinga\Module\Director\Db; +use Icinga\Module\Director\Objects\IcingaObject; +use Zend_Db_Select as ZfSelect; + +abstract class ObjectRestriction +{ + /** @var string */ + protected $name; + + /** @var \Zend_Db_Adapter_Abstract */ + protected $db; + + /** @var Auth */ + protected $auth; + + public function __construct(Db $connection, Auth $auth) + { + $this->db = $connection->getDbAdapter(); + $this->auth = $auth; + } + + abstract public function allows(IcingaObject $object); + + /** + * Apply the restriction to the given Hosts Query + * + * We assume that the query wants to fetch hosts and that therefore the + * icinga_host table already exists in the given query, using the $tableAlias + * alias. + * + * @param ZfSelect $query + * @param string $tableAlias + */ + abstract protected function filterQuery(ZfSelect $query, $tableAlias = 'o'); + + public function applyToQuery(ZfSelect $query, $tableAlias = 'o') + { + if ($this->isRestricted()) { + $this->filterQuery($query, $tableAlias); + } + + return $query; + } + + public function getName() + { + if ($this->name === null) { + throw new ProgrammingError('ObjectRestriction has no name'); + } + + return $this->name; + } + + public function isRestricted() + { + $restrictions = $this->auth->getRestrictions($this->getName()); + return ! empty($restrictions); + } + + protected function getQueryTableByAlias(ZfSelect $query, $tableAlias) + { + $from = $query->getPart(ZfSelect::FROM); + if (! array_key_exists($tableAlias, $from)) { + throw new ProgrammingError( + 'Cannot restrict query with alias "%s", got %s', + $tableAlias, + json_encode($from) + ); + } + + return $from[$tableAlias]['tableName']; + } + + protected function gracefullySplitOnComma($string) + { + return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); + } +} |