Merge branch 'bugfix/usergroup-search-does-not-work-10370'
fixes #10370 fixes #10367
This commit is contained in:
commit
7f5118a06a
|
@ -97,7 +97,7 @@ class GroupController extends AuthBackendController
|
|||
->from('group_membership', array('user_name'))
|
||||
->where('group_name', $groupName);
|
||||
|
||||
$this->setupFilterControl($members, null, array('user'));
|
||||
$this->setupFilterControl($members, null, array('user'), array('group'));
|
||||
$this->setupPaginationControl($members);
|
||||
$this->setupLimitControl();
|
||||
$this->setupSortControl(
|
||||
|
|
|
@ -99,7 +99,8 @@ class UserController extends AuthBackendController
|
|||
$this->setupFilterControl(
|
||||
$memberships,
|
||||
array('group_name' => t('User Group')),
|
||||
array('group_name')
|
||||
array('group'),
|
||||
array('user')
|
||||
);
|
||||
$this->setupPaginationControl($memberships);
|
||||
$this->setupLimitControl();
|
||||
|
@ -259,6 +260,7 @@ class UserController extends AuthBackendController
|
|||
$alreadySeen[$groupName] = null;
|
||||
$groups[] = (object) array(
|
||||
'group_name' => $groupName,
|
||||
'group' => $groupName,
|
||||
'backend' => $backend
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ use Icinga\Exception\ProgrammingError;
|
|||
use Icinga\Repository\LdapRepository;
|
||||
use Icinga\Repository\RepositoryQuery;
|
||||
use Icinga\Protocol\Ldap\LdapException;
|
||||
use Icinga\Protocol\Ldap\Expression;
|
||||
use Icinga\User;
|
||||
|
||||
class LdapUserBackend extends LdapRepository implements UserBackendInterface, Inspectable
|
||||
|
@ -203,12 +202,30 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
|
|||
$query = parent::select($columns);
|
||||
$query->getQuery()->setBase($this->baseDn);
|
||||
if ($this->filter) {
|
||||
$query->getQuery()->where(new Expression($this->filter));
|
||||
$query->getQuery()->setNativeFilter($this->filter);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this repository's virtual tables
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws ProgrammingError In case $this->userClass has not been set yet
|
||||
*/
|
||||
protected function initializeVirtualTables()
|
||||
{
|
||||
if ($this->userClass === null) {
|
||||
throw new ProgrammingError('It is required to set the object class where to find users first');
|
||||
}
|
||||
|
||||
return array(
|
||||
'user' => $this->userClass
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this repository's query columns
|
||||
*
|
||||
|
@ -318,29 +335,6 @@ class LdapUserBackend extends LdapRepository implements UserBackendInterface, In
|
|||
return ((int) $value) >= $bigBang->diff($now)->days;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the requested table exists
|
||||
*
|
||||
* This will return $this->userClass in case $table equals "user".
|
||||
*
|
||||
* @param string $table The table to validate
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
* (unused by the base implementation)
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws ProgrammingError In case the given table does not exist
|
||||
*/
|
||||
public function requireTable($table, RepositoryQuery $query = null)
|
||||
{
|
||||
$table = parent::requireTable($table, $query);
|
||||
if ($table === 'user') {
|
||||
$table = $this->userClass;
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the given column is a valid query target and return it or the actual name if it's an alias
|
||||
*
|
||||
|
|
|
@ -9,7 +9,7 @@ use Icinga\Application\Logger;
|
|||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Protocol\Ldap\Expression;
|
||||
use Icinga\Protocol\Ldap\LdapException;
|
||||
use Icinga\Repository\LdapRepository;
|
||||
use Icinga\Repository\RepositoryQuery;
|
||||
use Icinga\User;
|
||||
|
@ -368,12 +368,26 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
{
|
||||
$query = parent::select($columns);
|
||||
$query->getQuery()->setBase($this->groupBaseDn);
|
||||
if ($this->groupFilter) {
|
||||
// TODO(jom): This should differentiate between groups and their memberships
|
||||
$query->getQuery()->where(new Expression($this->groupFilter));
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize this repository's virtual tables
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws ProgrammingError In case $this->groupClass has not been set yet
|
||||
*/
|
||||
protected function initializeVirtualTables()
|
||||
{
|
||||
if ($this->groupClass === null) {
|
||||
throw new ProgrammingError('It is required to set the object class where to find groups first');
|
||||
}
|
||||
|
||||
return $query;
|
||||
return array(
|
||||
'group' => $this->groupClass,
|
||||
'group_membership' => $this->groupClass
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -420,7 +434,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
protected function initializeFilterColumns()
|
||||
{
|
||||
return array(
|
||||
t('Username') => 'user',
|
||||
t('Username') => 'user_name',
|
||||
t('User Group') => 'group_name',
|
||||
t('Created At') => 'created_at',
|
||||
t('Last Modified') => 'last_modified'
|
||||
|
@ -473,24 +487,28 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
*/
|
||||
protected function persistUserName($name)
|
||||
{
|
||||
$userDn = $this->ds
|
||||
->select()
|
||||
->from($this->userClass, array())
|
||||
->where($this->userNameAttribute, $name)
|
||||
->setUsePagedResults(false)
|
||||
->fetchDn();
|
||||
if ($userDn) {
|
||||
return $userDn;
|
||||
}
|
||||
try {
|
||||
$userDn = $this->ds
|
||||
->select()
|
||||
->from($this->userClass, array())
|
||||
->where($this->userNameAttribute, $name)
|
||||
->setUsePagedResults(false)
|
||||
->fetchDn();
|
||||
if ($userDn) {
|
||||
return $userDn;
|
||||
}
|
||||
|
||||
$groupDn = $this->ds
|
||||
->select()
|
||||
->from($this->groupClass, array())
|
||||
->where($this->groupNameAttribute, $name)
|
||||
->setUsePagedResults(false)
|
||||
->fetchDn();
|
||||
if ($groupDn) {
|
||||
return $groupDn;
|
||||
$groupDn = $this->ds
|
||||
->select()
|
||||
->from($this->groupClass, array())
|
||||
->where($this->groupNameAttribute, $name)
|
||||
->setUsePagedResults(false)
|
||||
->fetchDn();
|
||||
if ($groupDn) {
|
||||
return $groupDn;
|
||||
}
|
||||
} catch (LdapException $_) {
|
||||
// pass
|
||||
}
|
||||
|
||||
Logger::debug('Unable to persist uid or gid "%s" in repository "%s". No DN found.', $name, $this->getName());
|
||||
|
@ -516,11 +534,8 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
/**
|
||||
* Validate that the requested table exists
|
||||
*
|
||||
* This will return $this->groupClass in case $table equals "group" or "group_membership".
|
||||
*
|
||||
* @param string $table The table to validate
|
||||
* @param RepositoryQuery $query An optional query to pass as context
|
||||
* (unused by the base implementation)
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
|
@ -528,12 +543,11 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
*/
|
||||
public function requireTable($table, RepositoryQuery $query = null)
|
||||
{
|
||||
$table = parent::requireTable($table, $query);
|
||||
if ($table === 'group' || $table === 'group_membership') {
|
||||
$table = $this->groupClass;
|
||||
if ($query !== null && $table === 'group' && $this->groupFilter) {
|
||||
$query->getQuery()->setNativeFilter($this->groupFilter);
|
||||
}
|
||||
|
||||
return $table;
|
||||
return parent::requireTable($table, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -576,7 +590,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
->setBase($this->userBaseDn)
|
||||
->setUsePagedResults(false);
|
||||
if ($this->userFilter) {
|
||||
$userQuery->where(new Expression($this->userFilter));
|
||||
$userQuery->setNativeFilter($this->userFilter);
|
||||
}
|
||||
|
||||
if (($queryValue = $userQuery->fetchDn()) === null) {
|
||||
|
@ -590,7 +604,7 @@ class LdapUserGroupBackend extends LdapRepository implements UserGroupBackendInt
|
|||
->where($this->groupMemberAttribute, $queryValue)
|
||||
->setBase($this->groupBaseDn);
|
||||
if ($this->groupFilter) {
|
||||
$groupQuery->where(new Expression($this->groupFilter));
|
||||
$groupQuery->setNativeFilter($this->groupFilter);
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
|
|
|
@ -125,17 +125,26 @@ abstract class FilterChain extends Filter
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function listFilteredColumns()
|
||||
/**
|
||||
* List and return all column names referenced in this filter
|
||||
*
|
||||
* @param array $columns The columns listed so far
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listFilteredColumns(array $columns = array())
|
||||
{
|
||||
$columns = array();
|
||||
foreach ($this->filters as $filter) {
|
||||
if ($filter instanceof FilterExpression) {
|
||||
$columns[] = $filter->getColumn();
|
||||
$column= $filter->getColumn();
|
||||
if (! in_array($column, $columns, true)) {
|
||||
$columns[] = $column;
|
||||
}
|
||||
} else {
|
||||
$columns += $filter->listFilteredColumns();
|
||||
$columns = $filter->listFilteredColumns($columns);
|
||||
}
|
||||
}
|
||||
// array_unique?
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Protocol\Ldap;
|
||||
|
||||
class Expression
|
||||
{
|
||||
protected $value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->getValue();
|
||||
}
|
||||
}
|
|
@ -308,12 +308,8 @@ class LdapCapabilities
|
|||
ldap_error($ds)
|
||||
);
|
||||
}
|
||||
$cap = new LdapCapabilities(
|
||||
$connection->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
)
|
||||
);
|
||||
|
||||
$cap = new LdapCapabilities($connection->cleanupAttributes(ldap_get_attributes($ds, $entry), $fields));
|
||||
return $cap;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -694,18 +695,30 @@ class LdapConnection implements Selectable, Inspectable
|
|||
));
|
||||
} else {
|
||||
foreach ($query->getOrder() as $rule) {
|
||||
if (! in_array($rule[0], $fields)) {
|
||||
if (! in_array($rule[0], $fields, true)) {
|
||||
$fields[] = $rule[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
if ($unfoldAttribute) {
|
||||
foreach ($query->getFilter()->listFilteredColumns() as $filterColumn) {
|
||||
$fieldKey = array_search($filterColumn, $fields, true);
|
||||
if ($fieldKey === false || is_string($fieldKey)) {
|
||||
$fields[] = $filterColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$results = @ldap_search(
|
||||
$ds,
|
||||
$query->getBase() ?: $this->rootDn,
|
||||
(string) $query,
|
||||
array_values($fields),
|
||||
$unfoldAttribute
|
||||
? array_unique(array_values($fields))
|
||||
: array_values($fields),
|
||||
0, // Attributes and values
|
||||
$serverSorting && $limit ? $offset + $limit : 0
|
||||
);
|
||||
|
@ -727,25 +740,21 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$count = 0;
|
||||
$entries = array();
|
||||
$entry = ldap_first_entry($ds, $results);
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
do {
|
||||
if ($unfoldAttribute) {
|
||||
$rows = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields),
|
||||
$unfoldAttribute
|
||||
);
|
||||
|
||||
$rows = $this->cleanupAttributes(ldap_get_attributes($ds, $entry), $fields, $unfoldAttribute);
|
||||
if (is_array($rows)) {
|
||||
// TODO: Register the DN the same way as a section name in the ArrayDatasource!
|
||||
foreach ($rows as $row) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
if ($query->getFilter()->matches($row)) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -759,7 +768,7 @@ class LdapConnection implements Selectable, Inspectable
|
|||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
$fields
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -811,16 +820,25 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$serverSorting = false;//$this->getCapabilities()->hasOid(LdapCapabilities::LDAP_SERVER_SORT_OID);
|
||||
if (! $serverSorting && $query->hasOrder()) {
|
||||
foreach ($query->getOrder() as $rule) {
|
||||
if (! in_array($rule[0], $fields)) {
|
||||
if (! in_array($rule[0], $fields, true)) {
|
||||
$fields[] = $rule[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
if ($unfoldAttribute) {
|
||||
foreach ($query->getFilter()->listFilteredColumns() as $filterColumn) {
|
||||
$fieldKey = array_search($filterColumn, $fields, true);
|
||||
if ($fieldKey === false || is_string($fieldKey)) {
|
||||
$fields[] = $filterColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
$cookie = '';
|
||||
$entries = array();
|
||||
$unfoldAttribute = $query->getUnfoldAttribute();
|
||||
do {
|
||||
// Do not request the pagination control as a critical extension, as we want the
|
||||
// server to return results even if the paged search request cannot be satisfied
|
||||
|
@ -839,7 +857,9 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$ds,
|
||||
$base,
|
||||
$queryString,
|
||||
array_values($fields),
|
||||
$unfoldAttribute
|
||||
? array_unique(array_values($fields))
|
||||
: array_values($fields),
|
||||
0, // Attributes and values
|
||||
$serverSorting && $limit ? $offset + $limit : 0
|
||||
);
|
||||
|
@ -857,7 +877,8 @@ class LdapConnection implements Selectable, Inspectable
|
|||
} elseif (ldap_count_entries($ds, $results) === 0) {
|
||||
if (in_array(
|
||||
ldap_errno($ds),
|
||||
array(static::LDAP_SIZELIMIT_EXCEEDED, static::LDAP_ADMINLIMIT_EXCEEDED)
|
||||
array(static::LDAP_SIZELIMIT_EXCEEDED, static::LDAP_ADMINLIMIT_EXCEEDED),
|
||||
true
|
||||
)) {
|
||||
Logger::warning(
|
||||
'Unable to request more than %u results. Does the server allow paged search requests? (%s)',
|
||||
|
@ -872,22 +893,19 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$entry = ldap_first_entry($ds, $results);
|
||||
do {
|
||||
if ($unfoldAttribute) {
|
||||
$rows = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields),
|
||||
$unfoldAttribute
|
||||
);
|
||||
|
||||
$rows = $this->cleanupAttributes(ldap_get_attributes($ds, $entry), $fields, $unfoldAttribute);
|
||||
if (is_array($rows)) {
|
||||
// TODO: Register the DN the same way as a section name in the ArrayDatasource!
|
||||
foreach ($rows as $row) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
if ($query->getFilter()->matches($row)) {
|
||||
$count += 1;
|
||||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[] = $row;
|
||||
}
|
||||
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
if ($serverSorting && $limit > 0 && $limit === count($entries)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -901,7 +919,7 @@ class LdapConnection implements Selectable, Inspectable
|
|||
if (! $serverSorting || $offset === 0 || $offset < $count) {
|
||||
$entries[ldap_get_dn($ds, $entry)] = $this->cleanupAttributes(
|
||||
ldap_get_attributes($ds, $entry),
|
||||
array_flip($fields)
|
||||
$fields
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -963,8 +981,17 @@ class LdapConnection implements Selectable, Inspectable
|
|||
// necessary to create another array to map attributes case insensitively to their requested counterparts.
|
||||
// This does also apply the virtual alias handling. (Since an LDAP server does not handle such)
|
||||
$loweredFieldMap = array();
|
||||
foreach ($requestedFields as $name => $alias) {
|
||||
$loweredFieldMap[strtolower($name)] = is_string($alias) ? $alias : $name;
|
||||
foreach ($requestedFields as $alias => $name) {
|
||||
$loweredName = strtolower($name);
|
||||
if (isset($loweredFieldMap[$loweredName])) {
|
||||
if (! is_array($loweredFieldMap[$loweredName])) {
|
||||
$loweredFieldMap[$loweredName] = array($loweredFieldMap[$loweredName]);
|
||||
}
|
||||
|
||||
$loweredFieldMap[$loweredName][] = is_string($alias) ? $alias : $name;
|
||||
} else {
|
||||
$loweredFieldMap[$loweredName] = is_string($alias) ? $alias : $name;
|
||||
}
|
||||
}
|
||||
|
||||
$cleanedAttributes = array();
|
||||
|
@ -982,12 +1009,18 @@ class LdapConnection implements Selectable, Inspectable
|
|||
$requestedAttributeName = isset($loweredFieldMap[strtolower($attribute_name)])
|
||||
? $loweredFieldMap[strtolower($attribute_name)]
|
||||
: $attribute_name;
|
||||
$cleanedAttributes[$requestedAttributeName] = $attribute_value;
|
||||
if (is_array($requestedAttributeName)) {
|
||||
foreach ($requestedAttributeName as $requestedName) {
|
||||
$cleanedAttributes[$requestedName] = $attribute_value;
|
||||
}
|
||||
} else {
|
||||
$cleanedAttributes[$requestedAttributeName] = $attribute_value;
|
||||
}
|
||||
}
|
||||
|
||||
// The result may not contain all requested fields, so populate the cleaned
|
||||
// result with the missing fields and their value being set to null
|
||||
foreach ($requestedFields as $name => $alias) {
|
||||
foreach ($requestedFields as $alias => $name) {
|
||||
if (! is_string($alias)) {
|
||||
$alias = $name;
|
||||
}
|
||||
|
@ -1003,6 +1036,14 @@ class LdapConnection implements Selectable, Inspectable
|
|||
&& isset($cleanedAttributes[$unfoldAttribute])
|
||||
&& is_array($cleanedAttributes[$unfoldAttribute])
|
||||
) {
|
||||
$siblings = array();
|
||||
foreach ($loweredFieldMap as $loweredName => $requestedNames) {
|
||||
if (is_array($requestedNames) && in_array($unfoldAttribute, $requestedNames, true)) {
|
||||
$siblings = array_diff($requestedNames, array($unfoldAttribute));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$values = $cleanedAttributes[$unfoldAttribute];
|
||||
unset($cleanedAttributes[$unfoldAttribute]);
|
||||
$baseRow = (object) $cleanedAttributes;
|
||||
|
@ -1010,6 +1051,10 @@ class LdapConnection implements Selectable, Inspectable
|
|||
foreach ($values as $value) {
|
||||
$row = clone $baseRow;
|
||||
$row->{$unfoldAttribute} = $value;
|
||||
foreach ($siblings as $sibling) {
|
||||
$row->{$sibling} = $value;
|
||||
}
|
||||
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
|
@ -1184,6 +1229,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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
@ -42,12 +31,18 @@ class LdapQuery extends SimpleQuery
|
|||
*/
|
||||
protected $unfoldAttribute;
|
||||
|
||||
/**
|
||||
* This query's native LDAP filter
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $nativeFilter;
|
||||
|
||||
/**
|
||||
* Initialize this query
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$this->filters = array();
|
||||
$this->usePagedResults = false;
|
||||
}
|
||||
|
||||
|
@ -120,6 +115,29 @@ class LdapQuery extends SimpleQuery
|
|||
return $this->unfoldAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this query's native LDAP filter
|
||||
*
|
||||
* @param string $filter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setNativeFilter($filter)
|
||||
{
|
||||
$this->nativeFilter = $filter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this query's native LDAP filter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNativeFilter()
|
||||
{
|
||||
return $this->nativeFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose an objectClass and the columns you are interested in
|
||||
*
|
||||
|
@ -127,53 +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)
|
||||
{
|
||||
// TODO: Adjust this once support for Icinga\Data\Filter is available
|
||||
if ($condition instanceof Expression) {
|
||||
$this->filters[] = $condition;
|
||||
} else {
|
||||
$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
|
||||
*
|
||||
|
@ -225,36 +200,18 @@ 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');
|
||||
$filter = $this->ds->renderFilter($this->filter);
|
||||
if ($this->nativeFilter) {
|
||||
$filter = '(&(' . $this->nativeFilter . ')' . $filter . ')';
|
||||
}
|
||||
|
||||
$parts = array();
|
||||
foreach ($this->filters as $key => $value) {
|
||||
if ($value instanceof Expression) {
|
||||
$parts[] = (string) $value;
|
||||
} else {
|
||||
$parts[] = sprintf(
|
||||
'%s=%s',
|
||||
LdapUtils::quoteForSearch($key),
|
||||
LdapUtils::quoteForSearch($value, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($parts) > 1) {
|
||||
return '(&(' . implode(')(', $parts) . '))';
|
||||
} else {
|
||||
return '(' . $parts[0] . ')';
|
||||
}
|
||||
return $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -186,6 +186,7 @@ class Controller extends ModuleActionController
|
|||
* @param Filterable $filterable The filterable to create a filter editor for
|
||||
* @param array $filterColumns The filter columns to offer to the user
|
||||
* @param array $searchColumns The search columns to utilize for quick searches
|
||||
* @param array $preserveParams The url parameters to preserve
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
|
@ -194,25 +195,26 @@ class Controller extends ModuleActionController
|
|||
protected function setupFilterControl(
|
||||
Filterable $filterable,
|
||||
array $filterColumns = null,
|
||||
array $searchColumns = null
|
||||
array $searchColumns = null,
|
||||
array $preserveParams = null
|
||||
) {
|
||||
$editor = Widget::create('filterEditor')
|
||||
$defaultPreservedParams = array(
|
||||
'limit', // setupPaginationControl()
|
||||
'sort', // setupSortControl()
|
||||
'dir', // setupSortControl()
|
||||
'backend', // Framework
|
||||
'_dev' // Framework
|
||||
);
|
||||
|
||||
$editor = Widget::create('filterEditor');
|
||||
call_user_func_array(
|
||||
array($editor, 'preserveParams'),
|
||||
array_merge($defaultPreservedParams, $preserveParams ?: array())
|
||||
);
|
||||
|
||||
$editor
|
||||
->setQuery($filterable)
|
||||
->preserveParams(
|
||||
'limit',
|
||||
'sort',
|
||||
'dir',
|
||||
'format',
|
||||
'view',
|
||||
'user',
|
||||
'group',
|
||||
'backend',
|
||||
'stateType',
|
||||
'addColumns',
|
||||
'problems',
|
||||
'_dev'
|
||||
)
|
||||
->ignoreParams('page')
|
||||
->ignoreParams('page') // setupPaginationControl()
|
||||
->setColumns($filterColumns)
|
||||
->setSearchColumns($searchColumns)
|
||||
->handleRequest($this->getRequest());
|
||||
|
|
|
@ -574,7 +574,12 @@ class ListController extends Controller
|
|||
*/
|
||||
protected function filterQuery(DataView $dataView)
|
||||
{
|
||||
$this->setupFilterControl($dataView);
|
||||
$this->setupFilterControl($dataView, null, null, array(
|
||||
'format', // handleFormatRequest()
|
||||
'stateType', // hostsAction() and servicesAction()
|
||||
'addColumns', // addColumns()
|
||||
'problems' // servicegridAction()
|
||||
));
|
||||
$this->handleFormatRequest($dataView);
|
||||
return $dataView;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue