From 4341eef4b10a0a6101a1f87b70d5394b2197b5bb Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 9 Nov 2015 15:59:48 +0100 Subject: [PATCH] LdapQuery: Add support for Icinga\Data\Filter refs #10370 --- .../Icinga/Protocol/Ldap/LdapConnection.php | 92 ++++++++++++++++++- library/Icinga/Protocol/Ldap/LdapQuery.php | 77 +--------------- 2 files changed, 94 insertions(+), 75 deletions(-) diff --git a/library/Icinga/Protocol/Ldap/LdapConnection.php b/library/Icinga/Protocol/Ldap/LdapConnection.php index ceefc81b8..fe18c100e 100644 --- a/library/Icinga/Protocol/Ldap/LdapConnection.php +++ b/library/Icinga/Protocol/Ldap/LdapConnection.php @@ -7,13 +7,14 @@ use Exception; use ArrayIterator; use Icinga\Application\Config; use Icinga\Application\Logger; -use Icinga\Application\Platform; use Icinga\Data\ConfigObject; use Icinga\Data\Inspectable; use Icinga\Data\Inspection; use Icinga\Data\Selectable; use Icinga\Data\Sortable; -use Icinga\Exception\InspectionException; +use Icinga\Data\Filter\Filter; +use Icinga\Data\Filter\FilterChain; +use Icinga\Data\Filter\FilterExpression; use Icinga\Exception\ProgrammingError; use Icinga\Protocol\Ldap\LdapException; @@ -1184,6 +1185,93 @@ class LdapConnection implements Selectable, Inspectable return $dir; } + /** + * Render and return a valid LDAP filter representation of the given filter + * + * @param Filter $filter + * @param int $level + * + * @return string + */ + public function renderFilter(Filter $filter, $level = 0) + { + if ($filter->isExpression()) { + /** @var $filter FilterExpression */ + return $this->renderFilterExpression($filter); + } + + /** @var $filter FilterChain */ + $parts = array(); + foreach ($filter->filters() as $filterPart) { + $part = $this->renderFilter($filterPart, $level + 1); + if ($part) { + $parts[] = $part; + } + } + + if (empty($parts)) { + return ''; + } + + $format = '%1$s(%2$s)'; + if (count($parts) === 1) { + $format = '%2$s'; + } + if ($level === 0) { + $format = '(' . $format . ')'; + } + + return sprintf($format, $filter->getOperatorSymbol(), implode(')(', $parts)); + } + + /** + * Render and return a valid LDAP filter expression of the given filter + * + * @param FilterExpression $filter + * + * @return string + */ + protected function renderFilterExpression(FilterExpression $filter) + { + $column = $filter->getColumn(); + $sign = $filter->getSign(); + $expression = $filter->getExpression(); + $format = '%1$s%2$s%3$s'; + + if ($expression === null || $expression === true) { + $expression = '*'; + } elseif (is_array($expression)) { + $seqFormat = '|(%s)'; + if ($sign === '!=') { + $seqFormat = '!(' . $seqFormat . ')'; + $sign = '='; + } + + $seqParts = array(); + foreach ($expression as $expressionValue) { + $seqParts[] = sprintf( + $format, + LdapUtils::quoteForSearch($column), + $sign, + LdapUtils::quoteForSearch($expressionValue, true) + ); + } + + return sprintf($seqFormat, implode(')(', $seqParts)); + } + + if ($sign === '!=') { + $format = '!(%1$s=%3$s)'; + } + + return sprintf( + $format, + LdapUtils::quoteForSearch($column), + $sign, + LdapUtils::quoteForSearch($expression, true) + ); + } + /** * Inspect if this LDAP Connection is working as expected * diff --git a/library/Icinga/Protocol/Ldap/LdapQuery.php b/library/Icinga/Protocol/Ldap/LdapQuery.php index 3696062ef..bf37f72dd 100644 --- a/library/Icinga/Protocol/Ldap/LdapQuery.php +++ b/library/Icinga/Protocol/Ldap/LdapQuery.php @@ -4,23 +4,12 @@ namespace Icinga\Protocol\Ldap; use Icinga\Data\SimpleQuery; -use Icinga\Data\Filter\Filter; -use Icinga\Exception\NotImplementedError; /** * LDAP query class */ class LdapQuery extends SimpleQuery { - /** - * This query's filters - * - * Currently just a basic key/value pair based array. Can be removed once Icinga\Data\Filter is supported. - * - * @var array - */ - protected $filters; - /** * The base dn being used for this query * @@ -54,7 +43,6 @@ class LdapQuery extends SimpleQuery */ protected function init() { - $this->filters = array(); $this->usePagedResults = false; } @@ -157,47 +145,10 @@ class LdapQuery extends SimpleQuery */ public function from($target, array $fields = null) { - $this->filters['objectClass'] = $target; + $this->where('objectClass', $target); return parent::from($target, $fields); } - /** - * Add a new filter to the query - * - * @param string $condition Column to search in - * @param mixed $value Value to look for (asterisk wildcards are allowed) - * - * @return $this - */ - public function where($condition, $value = null) - { - $this->filters[$condition] = $value; - return $this; - } - - public function getFilter() - { - throw new NotImplementedError('Support for Icinga\Data\Filter is still missing. Use $this->where() instead'); - } - - public function addFilter(Filter $filter) - { - // TODO: This should be considered a quick fix only. - // Drop this entirely once support for Icinga\Data\Filter is available - if ($filter->isExpression()) { - $this->where($filter->getColumn(), $filter->getExpression()); - } elseif ($filter->isChain()) { - foreach ($filter->filters() as $chainOrExpression) { - $this->addFilter($chainOrExpression); - } - } - } - - public function setFilter(Filter $filter) - { - throw new NotImplementedError('Support for Icinga\Data\Filter is still missing. Use $this->where() instead'); - } - /** * Fetch result as tree * @@ -249,33 +200,13 @@ class LdapQuery extends SimpleQuery } /** - * Return the LDAP filter to be applied on this query + * Render and return this query's filter * * @return string - * - * @throws LdapException In case the objectClass filter does not exist */ - protected function renderFilter() + public function renderFilter() { - if (! isset($this->filters['objectClass'])) { - throw new LdapException('Object class is mandatory'); - } - - $parts = array(); - foreach ($this->filters as $key => $value) { - $parts[] = sprintf( - '%s=%s', - LdapUtils::quoteForSearch($key), - LdapUtils::quoteForSearch($value, true) - ); - } - - if (count($parts) > 1) { - $filter = '(&(' . implode(')(', $parts) . '))'; - } else { - $filter = '(' . $parts[0] . ')'; - } - + $filter = $this->ds->renderFilter($this->filter); if ($this->nativeFilter) { $filter = '(&(' . $this->nativeFilter . ')' . $filter . ')'; }