summaryrefslogtreecommitdiffstats
path: root/library/Director/Restriction
diff options
context:
space:
mode:
Diffstat (limited to 'library/Director/Restriction')
-rw-r--r--library/Director/Restriction/FilterByNameRestriction.php64
-rw-r--r--library/Director/Restriction/HostgroupRestriction.php171
-rw-r--r--library/Director/Restriction/MatchingFilter.php40
-rw-r--r--library/Director/Restriction/ObjectRestriction.php84
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);
+ }
+}