From e2e75ece2074bff6a89464d7f342752da7bc4a62 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Thu, 31 May 2018 08:58:30 +0200 Subject: [PATCH] added local filtering --- lam/HISTORY | 1 + lam/help/help.inc | 2 +- lam/lib/lists.inc | 55 ++++++++++++++++++++++++++++++++++++++++-- lam/lib/types/user.inc | 32 ++++++++++++++++-------- 4 files changed, 77 insertions(+), 13 deletions(-) diff --git a/lam/HISTORY b/lam/HISTORY index 12816934..2a485e28 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -2,6 +2,7 @@ June 2018 6.4 - Imagick PHP extension required - Passwords can be checked against external service (e.g. https://api.pwnedpasswords.com/range) - Personal/Windows: image cropping support + - Better filtering of account lists - Unix: Unix and group of names memberships can be synced in group selection - IMAP: create mailbox via file upload - PHP 7.2 support diff --git a/lam/help/help.inc b/lam/help/help.inc index 73db1ccf..36258b9e 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -174,7 +174,7 @@ $helpArray = array ( "249" => array ("Headline" => _('External password check'), "Text" => _('Please specify the URL (e.g. "https://api.pwnedpasswords.com/range/{SHA1PREFIX}") of your external password check.')), "250" => array ("Headline" => _("Filter"), - "Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-sensitive.")), + "Text" => _("Here you can input simple filter expressions (e.g. 'value' or 'v*'). The filter is case-insensitive.")), "260" => array ("Headline" => _("Additional LDAP filter"), "Text" => _('Use this to enter an additional LDAP filter (e.g. "(cn!=admin)") to reduce the number of visible elements for this account type.') . ' ' . _('You can use the wildcard @@LOGIN_DN@@ which will be substituted with the DN of the user who is currently logged in to LAM.') diff --git a/lam/lib/lists.inc b/lam/lib/lists.inc index dfe40f7d..f13fe11f 100644 --- a/lam/lib/lists.inc +++ b/lam/lib/lists.inc @@ -108,8 +108,8 @@ class lamList { const VIRTUAL_ATTRIBUTE_PREFIX = 'lam_virtual_'; const SERVER_SIDE_FILTER_ATTRIBUTES = array( - 'cn', 'uid', 'memberuid', 'description', - 'sn', 'surname', 'gn', 'givenname' + 'cn', 'commonname', 'uid', 'memberuid', 'description', + 'sn', 'surname', 'gn', 'givenname', 'company', 'mail' ); /** @@ -174,6 +174,8 @@ class lamList { if ($this->refresh) { $this->listRefreshData(); } + // local filtering + $this->applyLocalFilters(); // sort rows by sort column if (isset($this->entries)) { $this->listCreateSortMapping($this->entries); @@ -1033,6 +1035,55 @@ class lamList { return in_array(strtolower($attrName), lamList::SERVER_SIDE_FILTER_ATTRIBUTES); } + /** + * Applies any local filters for attributes that cannot be filtered server side. + */ + protected function applyLocalFilters() { + $this->entries = array(); + foreach ($this->ldapEntries as $index => &$data) { + $this->entries[$index] = &$data; + } + $toFilter = array(); + foreach ($this->filters as $filterAttribute => $filterValue) { + if ($this->isAttributeFilteredByServer($filterAttribute) || ($filterValue === '')) { + continue; + } + foreach ($this->entries as $index => &$data) { + if (in_array($index, $toFilter)) { + continue; + } + $regex = str_replace(array('*'), array('.*'), $filterValue); + $regex = '/^' . $regex . '$/i'; + if (!$this->isFilterMatching($data, $filterAttribute, $regex)) { + $toFilter[] = $index; + } + } + } + foreach ($toFilter as $index) { + unset($this->entries[$index]); + } + $this->entries = array_values($this->entries); + } + + /** + * Checks if the given LDAP data matches the filter. + * + * @param array $data LDAP attributes + * @param string $filterAttribute filter attribute name + * @param string $regex filter attribute regex + */ + protected function isFilterMatching(&$data, $filterAttribute, $regex) { + if (!isset($data[$filterAttribute])) { + return false; + } + foreach ($data[$filterAttribute] as $value) { + if (preg_match($regex, $value)) { + return true; + } + } + return false; + } + /** * Forces a refresh of the LDAP data. * Function must be called before $this->refresh option is checked to load new LDAP data (e.g. in listGetParams). diff --git a/lam/lib/types/user.inc b/lam/lib/types/user.inc index fb02cd96..0a4a4401 100644 --- a/lam/lib/types/user.inc +++ b/lam/lib/types/user.inc @@ -694,7 +694,7 @@ class lamUserList extends lamList { } // show account status if ($this->showAccountStatus) { - $this->injectAccountStatusAttributeAndFilterByStatus(); + $this->injectAccountStatusAttribute(); } } @@ -968,7 +968,7 @@ class lamUserList extends lamList { /** * Injects values for the virtual account status attribute to make it sortable. */ - private function injectAccountStatusAttributeAndFilterByStatus() { + private function injectAccountStatusAttribute() { $entryCount = sizeof($this->ldapEntries); for ($i = 0; $i < $entryCount; $i++) { $unixAvailable = self::isUnixAvailable($this->ldapEntries[$i]); @@ -1007,17 +1007,29 @@ class lamUserList extends lamList { elseif (!$hasUnlocked && $hasLocked) { $status = self::FILTER_LOCKED; } - // filter accounts - if (!empty($this->accountStatusFilter)) { - if ($status != $this->accountStatusFilter) { - unset($this->ldapEntries[$i]); - continue; - } - } // add virtual attribute $this->ldapEntries[$i][self::ATTR_ACCOUNT_STATUS][0] = $status; } - $this->ldapEntries = array_values($this->ldapEntries); + } + + /** + * {@inheritDoc} + * @see lamList::isFilterMatching() + */ + protected function isFilterMatching(&$data, $filterAttribute, $regex) { + if ($filterAttribute == self::ATTR_ACCOUNT_STATUS) { + return preg_match($regex, $data[self::ATTR_ACCOUNT_STATUS][0]); + } + if (($filterAttribute == 'gidnumber') && ($this->trans_primary == "on")) { + if (!isset($data[$filterAttribute])) { + return false; + } + if (!isset($this->trans_primary_hash[$data[$filterAttribute][0]])) { + return false; + } + return preg_match($regex, $this->trans_primary_hash[$data[$filterAttribute][0]]); + } + return parent::isFilterMatching($data, $filterAttribute, $regex); } /**