Restrictriction: add simple hostgroup restrictions

refs #832
This commit is contained in:
Thomas Gelf 2017-03-06 21:46:22 +01:00
parent cde328eeed
commit 02a3652c86
11 changed files with 293 additions and 2 deletions

View File

@ -8,8 +8,10 @@ use Icinga\Module\Director\Db\AppliedServiceSetLoader;
use Icinga\Module\Director\Exception\NestingError;
use Icinga\Module\Director\IcingaConfig\AgentWizard;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Objects\IcingaService;
use Icinga\Module\Director\Objects\IcingaServiceSet;
use Icinga\Module\Director\Restriction\BetaHostgroupRestriction;
use Icinga\Module\Director\Util;
use Icinga\Module\Director\Web\Controller\ObjectController;
use Icinga\Web\Url;
@ -47,6 +49,27 @@ class HostController extends ObjectController
$this->assertPermission('director/hosts');
}
protected function loadRestrictions()
{
return array(
$this->getHostgroupRestriction()
);
}
protected function getHostgroupRestriction()
{
return new BetaHostgroupRestriction($this->db(), $this->Auth());
}
/**
* @param IcingaHost $object
* @return bool
*/
protected function allowsObject(IcingaObject $object)
{
return $this->getHostgroupRestriction()->allowsHost($object);
}
public function editAction()
{
parent::editAction();

View File

@ -6,6 +6,8 @@ use Icinga\Data\Filter\Filter;
use Icinga\Data\Filter\FilterChain;
use Icinga\Data\Filter\FilterExpression;
use Icinga\Module\Director\Objects\IcingaHost;
use Icinga\Module\Director\Restriction\BetaHostgroupRestriction;
use Icinga\Module\Director\Tables\IcingaHostTable;
use Icinga\Module\Director\Web\Controller\ObjectsController;
class HostsController extends ObjectsController
@ -76,4 +78,14 @@ class HostsController extends ObjectsController
$this->setViewScript('objects/form');
}
/**
* @param IcingaHostTable $table
*/
protected function applyTableFilters($table)
{
$table->addObjectRestriction(
new BetaHostgroupRestriction($this->db(), $this->Auth())
);
}
}

View File

@ -2,6 +2,7 @@
namespace Icinga\Module\Director\Forms;
use Icinga\Module\Director\Restriction\BetaHostgroupRestriction;
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
class IcingaHostForm extends DirectorObjectForm
@ -132,6 +133,10 @@ class IcingaHostForm extends DirectorObjectForm
*/
protected function addGroupsElement()
{
if ($this->hasHostGroupRestriction()) {
return $this;
}
$groups = $this->enumHostgroups();
if (empty($groups)) {
return $this;
@ -153,6 +158,19 @@ class IcingaHostForm extends DirectorObjectForm
return $this;
}
protected function hasHostGroupRestriction()
{
foreach ($this->getObjectRestrictions() as $restriction) {
if ($restriction instanceof BetaHostgroupRestriction) {
if ($restriction->isRestricted()) {
return true;
}
}
}
return false;
}
/**
* @return $this
*/

View File

@ -2,16 +2,25 @@
namespace Icinga\Module\Director\Tables;
use Icinga\Module\Director\Restriction\ObjectRestriction;
use Icinga\Module\Director\Web\Table\QuickTable;
class IcingaHostTable extends QuickTable
{
protected $objectRestrictions = array();
protected $searchColumns = array(
'host',
'address',
'display_name'
);
public function addObjectRestriction(ObjectRestriction $restriction)
{
$this->objectRestrictions[$restriction->getName()] = $restriction;
return $this;
}
public function getColumns()
{
return array(
@ -58,10 +67,16 @@ class IcingaHostTable extends QuickTable
protected function getUnfilteredQuery()
{
return $this->db()->select()->from(
$query = $this->db()->select()->from(
array('h' => 'icinga_host'),
array()
)->order('h.object_name');
foreach ($this->objectRestrictions as $restriction) {
$restriction->applyToHostsQuery($query);
}
return $query;
}
public function getBaseQuery()

View File

@ -18,6 +18,14 @@ $this->providePermission(
);
$this->providePermission('director/*', $this->translate('Allow unrestricted access to Icinga Director'));
$this->provideRestriction(
'director/beta-filter/hostgroups',
$this->translate(
'BETA: Limit access to the given comma-separated list of hostgroups. This'
. ' restriction might change without pre-announcement'
)
);
$this->provideSearchUrl($this->translate('Host configs'), 'director/hosts?limit=10', 60);
/*

View File

@ -4,7 +4,9 @@ namespace Icinga\Module\Director\Dashboard;
use Countable;
use Exception;
use Icinga\Authentication\Auth;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Restriction\BetaHostgroupRestriction;
use Icinga\Web\View;
use Icinga\Module\Director\Dashboard\Dashlet\Dashlet;
use Icinga\Module\Director\Db;
@ -183,10 +185,40 @@ abstract class Dashboard implements Countable
}
}
return $this->db->getDbAdapter()->select()->from(
$query = $this->db->getDbAdapter()->select()->from(
array('o' => 'icinga_' . $type),
$columns
);
return $this->applyRestrictions($type, $query);
}
protected function applyRestrictions($type, $query)
{
switch ($type) {
case 'hostgroup':
$r = new BetaHostgroupRestriction($this->getDb(), $this->getAuth());
$r->applyToHostGroupsQuery($query);
break;
case 'host':
$r = new BetaHostgroupRestriction($this->getDb(), $this->getAuth());
$r->applyToHostsQuery($query, 'o.id');
break;
}
return $query;
}
protected function applyHostgroupRestrictions($query)
{
$restrictions = new BetaHostgroupRestriction($this->getDb(), $this->getAuth());
$restrictions->applyToHostGroupsQuery($query);
}
protected function getAuth()
{
return Auth::getInstance();
}
protected function getCntSql($objectType)

View File

@ -0,0 +1,80 @@
<?php
namespace Icinga\Module\Director\Restriction;
use Icinga\Module\Director\Objects\IcingaHost;
use Zend_Db_Select as ZfSelect;
class BetaHostgroupRestriction extends ObjectRestriction
{
protected $name = 'director/beta-filter/hostgroups';
public function allowsHost(IcingaHost $host)
{
if (! $this->isRestricted()) {
return true;
}
$query = $this->db->select()->from(
array('h' => 'icinga_host'),
array('id')
);
$this->applyToHostsQuery($query);
return (int) $this->db->fetchOne($query) === (int) $host->get('id');
}
public function applyToHostsQuery(ZfSelect $query, $hostIdColumn = 'h.id')
{
if (! $this->isRestricted()) {
return;
}
$groups = $this->listRestrictedHostgroups();
if (empty($groups)) {
$query->where('(1 = 0)');
} else {
$sub = $this->db->select()->from(
array('hgh' => 'icinga_hostgroup_host'),
array('e' => '(1)')
)->join(
array('hg' => 'icinga_hostgroup'),
'hgh.hostgroup_id = hg.id'
)->where('hgh.host_id = ' . $hostIdColumn)
->where('hg.object_name IN (?)', $groups);
$query->where('EXISTS ?', $sub);
}
}
public function applyToHostGroupsQuery(ZfSelect $query)
{
if (! $this->isRestricted()) {
return;
}
$groups = $this->listRestrictedHostgroups();
if (empty($groups)) {
$query->where('(1 = 0)');
} else {
$query->where('object_name IN (?)', $groups);
}
}
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;
}
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Icinga\Module\Director\Restriction;
use Icinga\Authentication\Auth;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Director\Db;
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;
}
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 gracefullySplitOnComma($string)
{
return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY);
}
}

View File

@ -22,6 +22,16 @@ abstract class ObjectController extends ActionController
'endpoint'
);
protected function loadRestrictions()
{
return array();
}
protected function allowsObject(IcingaObject $object)
{
return true;
}
public function init()
{
parent::init();
@ -169,6 +179,7 @@ abstract class ObjectController extends ActionController
->setDb($this->db())
->setApi($this->getApiIfAvailable());
$form->setObject($object);
$form->setObjectRestrictions($this->loadRestrictions());
$this->view->title = $object->object_name;
$this->view->form->handleRequest();
@ -334,6 +345,11 @@ abstract class ObjectController extends ActionController
$name,
$this->db()
);
if (! $this->allowsObject($this->object)) {
$this->object = null;
throw new NotFoundError('No such object available');
}
} elseif ($id = $this->params->get('id')) {
$this->object = IcingaObject::loadByType(
$this->getType(),

View File

@ -142,6 +142,8 @@ abstract class ObjectsController extends ActionController
}
}
$this->applyTableFilters($table);
$this->view->title = $title;
$this->view->addLink = $this->view->qlink(
@ -155,6 +157,10 @@ abstract class ObjectsController extends ActionController
$this->setViewScript('objects/table');
}
protected function applyTableFilters($table)
{
}
public function editAction()
{
$type = ucfirst($this->getType());

View File

@ -11,6 +11,7 @@ use Icinga\Module\Director\Exception\NestingError;
use Icinga\Module\Director\IcingaConfig\StateFilterSet;
use Icinga\Module\Director\IcingaConfig\TypeFilterSet;
use Icinga\Module\Director\Objects\IcingaObject;
use Icinga\Module\Director\Restriction\ObjectRestriction;
use Icinga\Module\Director\Util;
use Zend_Form_Element as ZfElement;
use Zend_Form_Element_Select as ZfSelect;
@ -49,6 +50,9 @@ abstract class DirectorObjectForm extends QuickForm
private $presetImports;
/** @var ObjectRestriction[] */
private $objectRestrictions = array();
private $earlyProperties = array(
'imports',
'check_command',
@ -561,6 +565,38 @@ abstract class DirectorObjectForm extends QuickForm
return $this;
}
/**
* @param ObjectRestriction[] $restrictions
* @return $this
*/
public function setObjectRestrictions(array $restrictions)
{
$this->objectRestrictions = array();
foreach ($restrictions as $restriction) {
$this->addObjectRestriction($restriction);
}
return $this;
}
/**
* @param ObjectRestriction $restriction
* @return $this
*/
public function addObjectRestriction(ObjectRestriction $restriction)
{
$this->objectRestrictions[$restriction->getName()] = $restriction;
return $this;
}
/**
* @return ObjectRestriction[]
*/
public function getObjectRestrictions()
{
return $this->objectRestrictions;
}
protected function getObjectClassname()
{
if ($this->className === null) {