summaryrefslogtreecommitdiffstats
path: root/apps/user_ldap
diff options
context:
space:
mode:
Diffstat (limited to 'apps/user_ldap')
-rw-r--r--apps/user_ldap/group_ldap.php83
-rw-r--r--apps/user_ldap/group_proxy.php11
-rw-r--r--apps/user_ldap/l10n/ca.php3
-rw-r--r--apps/user_ldap/l10n/nl.php1
-rw-r--r--apps/user_ldap/lib/access.php55
-rw-r--r--apps/user_ldap/tests/group_ldap.php115
6 files changed, 252 insertions, 16 deletions
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php
index 4f2424d9531..40d9dec1410 100644
--- a/apps/user_ldap/group_ldap.php
+++ b/apps/user_ldap/group_ldap.php
@@ -277,6 +277,84 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
}
/**
+ * @brief returns the number of users in a group, who match the search term
+ * @param string the internal group name
+ * @param string optional, a search string
+ * @returns int | bool
+ */
+ public function countUsersInGroup($gid, $search = '') {
+ $cachekey = 'countUsersInGroup-'.$gid.'-'.$search;
+ if(!$this->enabled || !$this->groupExists($gid)) {
+ return false;
+ }
+ $groupUsers = $this->access->connection->getFromCache($cachekey);
+ if(!is_null($groupUsers)) {
+ return $groupUsers;
+ }
+
+ $groupDN = $this->access->groupname2dn($gid);
+ if(!$groupDN) {
+ // group couldn't be found, return empty resultset
+ $this->access->connection->writeToCache($cachekey, false);
+ return false;
+ }
+
+ $members = array_keys($this->_groupMembers($groupDN));
+ if(!$members) {
+ //in case users could not be retrieved, return empty resultset
+ $this->access->connection->writeToCache($cachekey, false);
+ return false;
+ }
+
+ if(empty($search)) {
+ $groupUsers = count($members);
+ $this->access->connection->writeToCache($cachekey, $groupUsers);
+ return $groupUsers;
+ }
+ $isMemberUid =
+ (strtolower($this->access->connection->ldapGroupMemberAssocAttr)
+ === 'memberuid');
+
+ //we need to apply the search filter
+ //alternatives that need to be checked:
+ //a) get all users by search filter and array_intersect them
+ //b) a, but only when less than 1k 10k ?k users like it is
+ //c) put all DNs|uids in a LDAP filter, combine with the search string
+ // and let it count.
+ //For now this is not important, because the only use of this method
+ //does not supply a search string
+ $groupUsers = array();
+ foreach($members as $member) {
+ if($isMemberUid) {
+ //we got uids, need to get their DNs to 'tranlsate' them to usernames
+ $filter = $this->access->combineFilterWithAnd(array(
+ \OCP\Util::mb_str_replace('%uid', $member,
+ $this->access->connection->ldapLoginFilter, 'UTF-8'),
+ $this->access->getFilterPartForUserSearch($search)
+ ));
+ $ldap_users = $this->access->fetchListOfUsers($filter, 'dn');
+ if(count($ldap_users) < 1) {
+ continue;
+ }
+ $groupUsers[] = $this->access->dn2username($ldap_users[0]);
+ } else {
+ //we need to apply the search filter now
+ if(!$this->access->readAttribute($member,
+ $this->access->connection->ldapUserDisplayName,
+ $this->access->getFilterPartForUserSearch($search))) {
+ continue;
+ }
+ // dn2username will also check if the users belong to the allowed base
+ if($ocname = $this->access->dn2username($member)) {
+ $groupUsers[] = $ocname;
+ }
+ }
+ }
+
+ return count($groupUsers);
+ }
+
+ /**
* @brief get a list of all display names in a group
* @returns array with display names (value) and user ids(key)
*/
@@ -418,6 +496,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
* compared with OC_USER_BACKEND_CREATE_USER etc.
*/
public function implementsActions($actions) {
- return (bool)(OC_GROUP_BACKEND_GET_DISPLAYNAME & $actions);
+ return (bool)((
+ OC_GROUP_BACKEND_GET_DISPLAYNAME
+ | OC_GROUP_BACKEND_COUNT_USERS
+ ) & $actions);
}
}
diff --git a/apps/user_ldap/group_proxy.php b/apps/user_ldap/group_proxy.php
index 4404bd7fe3a..c0009736239 100644
--- a/apps/user_ldap/group_proxy.php
+++ b/apps/user_ldap/group_proxy.php
@@ -145,6 +145,17 @@ class Group_Proxy extends lib\Proxy implements \OCP\GroupInterface {
}
/**
+ * @brief returns the number of users in a group, who match the search term
+ * @param string the internal group name
+ * @param string optional, a search string
+ * @returns int | bool
+ */
+ public function countUsersInGroup($gid, $search = '') {
+ return $this->handleRequest(
+ $gid, 'countUsersInGroup', array($gid, $search));
+ }
+
+ /**
* @brief get a list of all display names in a group
* @returns array with display names (value) and user ids(key)
*/
diff --git a/apps/user_ldap/l10n/ca.php b/apps/user_ldap/l10n/ca.php
index 98b1cf74640..c8542586f82 100644
--- a/apps/user_ldap/l10n/ca.php
+++ b/apps/user_ldap/l10n/ca.php
@@ -70,6 +70,7 @@ $TRANSLATIONS = array(
"Backup (Replica) Port" => "Port de la còpia de seguretat (rèplica)",
"Disable Main Server" => "Desactiva el servidor principal",
"Only connect to the replica server." => "Connecta només al servidor rèplica.",
+"Case insensitive LDAP server (Windows)" => "Servidor LDAP sense distinció entre majúscules i minúscules (Windows)",
"Turn off SSL certificate validation." => "Desactiva la validació de certificat SSL.",
"Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "No es recomana, useu-ho només com a prova! Importeu el certificat SSL del servidor LDAP al servidor %s només si la connexió funciona amb aquesta opció.",
"Cache Time-To-Live" => "Memòria de cau Time-To-Live",
@@ -89,6 +90,8 @@ $TRANSLATIONS = array(
"Group-Member association" => "Associació membres-grup",
"Nested Groups" => "Grups imbricats",
"When switched on, groups that contain groups are supported. (Only works if the group member attribute contains DNs.)" => "Quan està activat, els grups que contenen grups estan permesos. (Només funciona si l'atribut del grup membre conté DNs.)",
+"Paging chunksize" => "Mida de la pàgina",
+"Chunksize used for paged LDAP searches that may return bulky results like user or group enumeration. (Setting it 0 disables paged LDAP searches in those situations.)" => "Mida usada per cerques LDAP paginades que podrien retornar respostes de volcat com enumeració d'usuari o grup. (Establint-ho a 0 desactiva les cerques LDAP paginades en aquestes situacions.)",
"Special Attributes" => "Atributs especials",
"Quota Field" => "Camp de quota",
"Quota Default" => "Quota per defecte",
diff --git a/apps/user_ldap/l10n/nl.php b/apps/user_ldap/l10n/nl.php
index 5698f8a6f1d..196e5a52d12 100644
--- a/apps/user_ldap/l10n/nl.php
+++ b/apps/user_ldap/l10n/nl.php
@@ -70,6 +70,7 @@ $TRANSLATIONS = array(
"Backup (Replica) Port" => "Backup (Replica) Poort",
"Disable Main Server" => "Deactiveren hoofdserver",
"Only connect to the replica server." => "Maak alleen een verbinding met de replica server.",
+"Case insensitive LDAP server (Windows)" => "Niet-hoofdlettergevoelige LDAP server (Windows)",
"Turn off SSL certificate validation." => "Schakel SSL certificaat validatie uit.",
"Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." => "Niet aanbevolen, gebruik alleen om te testen! Als de connectie alleen werkt met deze optie, importeer dan het SSL-certificaat van de LDAP-server naar uw %s server.",
"Cache Time-To-Live" => "Cache time-to-live",
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 4d187bab8d5..0b3ff4aa15e 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -659,7 +659,7 @@ class Access extends LDAPUtility {
* @param string $filter
*/
public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
- return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
+ return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
}
/**
@@ -775,22 +775,47 @@ class Access extends LDAPUtility {
*/
private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
\OCP\Util::writeLog('user_ldap', 'Count filter: '.print_r($filter, true), \OCP\Util::DEBUG);
- $search = $this->executeSearch($filter, $base, $attr, $limit, $offset);
- if($search === false) {
- return false;
+
+ if(is_null($limit)) {
+ $limit = $this->connection->ldapPagingSize;
}
- list($sr, $pagedSearchOK) = $search;
- $cr = $this->connection->getConnectionResource();
+
$counter = 0;
- foreach($sr as $key => $res) {
- $count = $this->ldap->countEntries($cr, $res);
- if($count !== false) {
- $counter += $count;
+ $count = null;
+ $cr = $this->connection->getConnectionResource();
+
+ do {
+ $continue = false;
+ $search = $this->executeSearch($filter, $base, $attr,
+ $limit, $offset);
+ if($search === false) {
+ return $counter > 0 ? $counter : false;
}
- }
+ list($sr, $pagedSearchOK) = $search;
+
+ $count = $this->countEntriesInSearchResults($sr, $limit, $continue);
+ $counter += $count;
- $this->processPagedSearchStatus($sr, $filter, $base, $counter, $limit,
+ $this->processPagedSearchStatus($sr, $filter, $base, $count, $limit,
$offset, $pagedSearchOK, $skipHandling);
+ $offset += $limit;
+ } while($continue);
+
+ return $counter;
+ }
+
+ private function countEntriesInSearchResults($searchResults, $limit,
+ &$hasHitLimit) {
+ $cr = $this->connection->getConnectionResource();
+ $count = 0;
+
+ foreach($searchResults as $res) {
+ $count = intval($this->ldap->countEntries($cr, $res));
+ $counter += $count;
+ if($count === $limit) {
+ $hasHitLimit = true;
+ }
+ }
return $counter;
}
@@ -891,7 +916,7 @@ class Access extends LDAPUtility {
//we slice the findings, when
//a) paged search insuccessful, though attempted
//b) no paged search, but limit set
- if((!$this->pagedSearchedSuccessful
+ if((!$this->getPagedSearchResultState()
&& $pagedSearchOK)
|| (
!$pagedSearchOK
@@ -1184,7 +1209,7 @@ class Access extends LDAPUtility {
}
$offset -= $limit;
//we work with cache here
- $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset;
+ $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
$cookie = '';
if(isset($this->cookies[$cachekey])) {
$cookie = $this->cookies[$cachekey];
@@ -1206,7 +1231,7 @@ class Access extends LDAPUtility {
*/
private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
if(!empty($cookie)) {
- $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .$limit . '-' . $offset;
+ $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
$this->cookies[$cachekey] = $cookie;
}
}
diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php
new file mode 100644
index 00000000000..ecbd42319e3
--- /dev/null
+++ b/apps/user_ldap/tests/group_ldap.php
@@ -0,0 +1,115 @@
+<?php
+/**
+* ownCloud
+*
+* @author Arthur Schiwon
+* @copyright 2014 Arthur Schiwon <blizzz@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library. If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+namespace OCA\user_ldap\tests;
+
+namespace OCA\user_ldap\tests;
+
+use \OCA\user_ldap\GROUP_LDAP as GroupLDAP;
+use \OCA\user_ldap\lib\Access;
+use \OCA\user_ldap\lib\Connection;
+use \OCA\user_ldap\lib\ILDAPWrapper;
+
+class Test_Group_Ldap extends \PHPUnit_Framework_TestCase {
+ private function getAccessMock() {
+ static $conMethods;
+ static $accMethods;
+
+ if(is_null($conMethods) || is_null($accMethods)) {
+ $conMethods = get_class_methods('\OCA\user_ldap\lib\Connection');
+ $accMethods = get_class_methods('\OCA\user_ldap\lib\Access');
+ }
+ $lw = $this->getMock('\OCA\user_ldap\lib\ILDAPWrapper');
+ $connector = $this->getMock('\OCA\user_ldap\lib\Connection',
+ $conMethods,
+ array($lw, null, null));
+ $access = $this->getMock('\OCA\user_ldap\lib\Access',
+ $accMethods,
+ array($connector, $lw));
+
+ return $access;
+ }
+
+ private function enableGroups($access) {
+ $access->connection->expects($this->any())
+ ->method('__get')
+ ->will($this->returnCallback(function($name) {
+// if($name === 'ldapLoginFilter') {
+// return '%uid';
+// }
+ return 1;
+ }));
+ }
+
+ public function testCountEmptySearchString() {
+ $access = $this->getAccessMock();
+
+ $this->enableGroups($access);
+
+ $access->expects($this->any())
+ ->method('groupname2dn')
+ ->will($this->returnValue('cn=group,dc=foo,dc=bar'));
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnValue(array('u11', 'u22', 'u33', 'u34')));
+
+ $groupBackend = new GroupLDAP($access);
+ $users = $groupBackend->countUsersInGroup('group');
+
+ $this->assertSame(4, $users);
+ }
+
+ public function testCountWithSearchString() {
+ $access = $this->getAccessMock();
+
+ $this->enableGroups($access);
+
+ $access->expects($this->any())
+ ->method('groupname2dn')
+ ->will($this->returnValue('cn=group,dc=foo,dc=bar'));
+
+ $access->expects($this->any())
+ ->method('readAttribute')
+ ->will($this->returnCallback(function($name) {
+ //the search operation will call readAttribute, thus we need
+ //to anaylze the "dn". All other times we just need to return
+ //something that is neither null or false, but once an array
+ //with the users in the group – so we do so all other times for
+ //simplicicity.
+ if(strpos($name, 'u') === 0) {
+ return strpos($name, '3');
+ }
+ return array('u11', 'u22', 'u33', 'u34');
+ }));
+
+ $access->expects($this->any())
+ ->method('dn2username')
+ ->will($this->returnValue('foobar'));
+
+ $groupBackend = new GroupLDAP($access);
+ $users = $groupBackend->countUsersInGroup('group', '3');
+
+ $this->assertSame(2, $users);
+ }
+
+} \ No newline at end of file