From 48e426b589029b8e616be2785afc8c0b4b4aecaf Mon Sep 17 00:00:00 2001 From: root Date: Fri, 6 Dec 2013 16:46:52 +0100 Subject: [PATCH 1/7] add support for nested groups --- apps/user_ldap/group_ldap.php | 79 +++++++++++++++++++++++---- apps/user_ldap/lib/configuration.php | 3 + apps/user_ldap/templates/settings.php | 1 + 3 files changed, 73 insertions(+), 10 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 32e2cec596..0a5ab19276 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -61,8 +61,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = $this->access->readAttribute($dn_group, - $this->access->connection->ldapGroupMemberAssocAttr); + $members = array_keys($this->_groupMembers($dn_group)); if(!$members) { $this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false); return false; @@ -89,6 +88,39 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $isInGroup; } + private function _groupMembers($dn_group, &$groups_seen = null) { + if ($groups_seen == null) { + $groups_seen = array(); + } + $all_members = array(); + if (array_key_exists($dn_group, $groups_seen)) { + // avoid loops + return array(); + } + // used extensively in cron job, caching makes sense for nested groups + $cache_key = '_groupMembers'.$dn_group; + if($this->access->connection->isCached($cache_key)) { + \OCP\Util::writeLog('user_ldap', 'LEO _groupMembers('.$dn_group.') using cached value', \OCP\Util::DEBUG); + return $this->access->connection->getFromCache($cache_key); + } + $groups_seen[$dn_group] = 1; + $members = $this->access->readAttribute($dn_group, $this->access->connection->ldapGroupMemberAssocAttr, + $this->access->connection->ldapGroupFilter); + if ($members) { + foreach ($members as $member_dn) { + $all_members[$member_dn] = 1; + if ($this->access->connection->ldapNestedGroups) { + $submembers = $this->_groupMembers($member_dn, $groups_seen); + if ($submembers) { + $all_members = array_merge($all_members, $submembers); + } + } + } + } + $this->access->connection->writeToCache($cache_key, $all_members); + return $all_members; + } + /** * @brief Get all groups a user belongs to * @param $uid Name of the user @@ -124,18 +156,45 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $uid = $userDN; } - $filter = $this->access->combineFilterWithAnd(array( - $this->access->connection->ldapGroupFilter, - $this->access->connection->ldapGroupMemberAssocAttr.'='.$uid - )); - $groups = $this->access->fetchListOfGroups($filter, - array($this->access->connection->ldapGroupDisplayName, 'dn')); + $groups = array_values($this->_getGroupsByMember($uid)); $groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + \OCP\Util::writeLog('user_ldap', 'LEO _getGroupsByMember('.$uid.'): '.implode(", ", $groups), \OCP\Util::DEBUG); $this->access->connection->writeToCache($cacheKey, $groups); return $groups; } + /* private */ public function _getGroupsByMember($dn, &$seen = null) { + if ($seen == null) { + $seen = array(); + } + $all_groups = array(); + if (array_key_exists($dn, $seen)) { + // avoid loops + return array(); + } + $seen[$dn] = 1; + $filter = $this->combineFilterWithAnd(array( + $this->access->connection->ldapGroupFilter, + $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn + )); + $groups = $this->fetchListOfGroups($filter, + array($this->access->connection->ldapGroupDisplayName, 'dn')); + if ($groups) { + foreach ($groups as $groupobj) { + $group_dn = $groupobj['dn']; + $all_groups[$group_dn] = $groupobj; + if ($this->access->connection->ldapNestedGroups) { + $supergroups = $this->_getGroupsByMember($group_dn, $seen); + if ($supergroups) { + $all_groups = array_merge($all_groups, $supergroups); + } + } + } + } + return $all_groups; + } + /** * @brief get a list of all users in a group * @returns array with user ids @@ -172,8 +231,8 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return array(); } - $members = $this->access->readAttribute($groupDN, - $this->access->connection->ldapGroupMemberAssocAttr); + $members = array_keys($this->_groupMembers($groupDN)); + \OCP\Util::writeLog('user_ldap', 'LEO _groupMembers('.$groupDN.'): '.implode(", ", $members), \OCP\Util::DEBUG); if(!$members) { //in case users could not be retrieved, return empty resultset $this->access->connection->writeToCache($cachekey, array()); diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php index 874082f78f..bceacdfdf8 100644 --- a/apps/user_ldap/lib/configuration.php +++ b/apps/user_ldap/lib/configuration.php @@ -76,6 +76,7 @@ class Configuration { 'ldapExpertUUIDUserAttr' => null, 'ldapExpertUUIDGroupAttr' => null, 'lastJpegPhotoLookup' => null, + 'ldapNestedGroups' => false, ); public function __construct($configPrefix, $autoread = true) { @@ -338,6 +339,7 @@ class Configuration { 'ldap_expert_uuid_group_attr' => '', 'has_memberof_filter_support' => 0, 'last_jpegPhoto_lookup' => 0, + 'ldap_nested_groups' => 0, ); } @@ -389,6 +391,7 @@ class Configuration { 'ldap_expert_uuid_group_attr' => 'ldapExpertUUIDGroupAttr', 'has_memberof_filter_support' => 'hasMemberOfFilterSupport', 'last_jpegPhoto_lookup' => 'lastJpegPhotoLookup', + 'ldap_nested_groups' => 'ldapNestedGroups', ); return $array; } diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 3ccc7a860f..ad527c0634 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -58,6 +58,7 @@

t('Username-LDAP User Mapping'));?>

t('Usernames are used to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage.'));?>


+

From 86809be638a4dea6907322d24de618a1047bd9f0 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 8 Dec 2013 15:49:25 +0100 Subject: [PATCH 2/7] combineFilterWithAnd recently moved to Access --- apps/user_ldap/group_ldap.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 0a5ab19276..9286ece7ee 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -174,12 +174,12 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return array(); } $seen[$dn] = 1; - $filter = $this->combineFilterWithAnd(array( + $filter = $this->access->combineFilterWithAnd(array( $this->access->connection->ldapGroupFilter, $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn )); - $groups = $this->fetchListOfGroups($filter, - array($this->access->connection->ldapGroupDisplayName, 'dn')); + $groups = $this->access->fetchListOfGroups($filter, + array($this->access->connection->ldapGroupDisplayName, 'dn')); if ($groups) { foreach ($groups as $groupobj) { $group_dn = $groupobj['dn']; From 4fb16804d6c0ac12b47efaa4c50234538aa93874 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Dec 2013 18:47:59 +0100 Subject: [PATCH 3/7] move nested group checkbox to directory settings --- apps/user_ldap/templates/settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index ad527c0634..79c4ae224c 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -36,6 +36,7 @@

+

t('Special Attributes'));?>

@@ -58,7 +59,6 @@

t('Username-LDAP User Mapping'));?>

t('Usernames are used to store and assign (meta) data. In order to precisely identify and recognize users, each LDAP user will have a internal username. This requires a mapping from username to LDAP user. The created username is mapped to the UUID of the LDAP user. Additionally the DN is cached as well to reduce LDAP interaction, but it is not used for identification. If the DN changes, the changes will be found. The internal username is used all over. Clearing the mappings will have leftovers everywhere. Clearing the mappings is not configuration sensitive, it affects all LDAP configurations! Never clear the mappings in a production environment, only in a testing or experimental stage.'));?>


-

From a18cff44795c5888c993f83b89b8bd7f0d0b4af6 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Dec 2013 19:02:01 +0100 Subject: [PATCH 4/7] remove debug output --- apps/user_ldap/group_ldap.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 9286ece7ee..1300a280b0 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -100,7 +100,6 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { // used extensively in cron job, caching makes sense for nested groups $cache_key = '_groupMembers'.$dn_group; if($this->access->connection->isCached($cache_key)) { - \OCP\Util::writeLog('user_ldap', 'LEO _groupMembers('.$dn_group.') using cached value', \OCP\Util::DEBUG); return $this->access->connection->getFromCache($cache_key); } $groups_seen[$dn_group] = 1; @@ -158,7 +157,6 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $groups = array_values($this->_getGroupsByMember($uid)); $groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING); - \OCP\Util::writeLog('user_ldap', 'LEO _getGroupsByMember('.$uid.'): '.implode(", ", $groups), \OCP\Util::DEBUG); $this->access->connection->writeToCache($cacheKey, $groups); return $groups; @@ -232,7 +230,6 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } $members = array_keys($this->_groupMembers($groupDN)); - \OCP\Util::writeLog('user_ldap', 'LEO _groupMembers('.$groupDN.'): '.implode(", ", $members), \OCP\Util::DEBUG); if(!$members) { //in case users could not be retrieved, return empty resultset $this->access->connection->writeToCache($cachekey, array()); From 2b127a6ac5c4da7a34c49f87878b643dafc5e40d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Dec 2013 10:43:48 +0100 Subject: [PATCH 5/7] fix indentation use identity test where appropriate use camelcase variable names _getGroupsByMember is a private function --- apps/user_ldap/group_ldap.php | 82 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 1300a280b0..fb47de7d94 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -88,37 +88,37 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $isInGroup; } - private function _groupMembers($dn_group, &$groups_seen = null) { - if ($groups_seen == null) { - $groups_seen = array(); + private function _groupMembers($dnGroup, &$seen = null) { + if ($seen === null) { + $seen = array(); } - $all_members = array(); - if (array_key_exists($dn_group, $groups_seen)) { + $allMembers = array(); + if (array_key_exists($dnGroup, $seen)) { // avoid loops return array(); } // used extensively in cron job, caching makes sense for nested groups - $cache_key = '_groupMembers'.$dn_group; - if($this->access->connection->isCached($cache_key)) { - return $this->access->connection->getFromCache($cache_key); + $cacheKey = '_groupMembers'.$dnGroup; + if($this->access->connection->isCached($cacheKey)) { + return $this->access->connection->getFromCache($cacheKey); } - $groups_seen[$dn_group] = 1; - $members = $this->access->readAttribute($dn_group, $this->access->connection->ldapGroupMemberAssocAttr, - $this->access->connection->ldapGroupFilter); + $seen[$dnGroup] = 1; + $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr, + $this->access->connection->ldapGroupFilter); if ($members) { - foreach ($members as $member_dn) { - $all_members[$member_dn] = 1; - if ($this->access->connection->ldapNestedGroups) { - $submembers = $this->_groupMembers($member_dn, $groups_seen); - if ($submembers) { - $all_members = array_merge($all_members, $submembers); - } - } - } + foreach ($members as $memberDN) { + $allMembers[$memberDN] = 1; + if ($this->access->connection->ldapNestedGroups) { + $subMembers = $this->_groupMembers($memberDN, $seen); + if ($subMembers) { + $allMembers = array_merge($allMembers, $subMembers); + } + } + } } - $this->access->connection->writeToCache($cache_key, $all_members); - return $all_members; - } + $this->access->connection->writeToCache($cacheKey, $allMembers); + return $allMembers; + } /** * @brief Get all groups a user belongs to @@ -162,11 +162,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { return $groups; } - /* private */ public function _getGroupsByMember($dn, &$seen = null) { - if ($seen == null) { - $seen = array(); + private function _getGroupsByMember($dn, &$seen = null) { + if ($seen === null) { + $seen = array(); } - $all_groups = array(); + $allGroups = array(); if (array_key_exists($dn, $seen)) { // avoid loops return array(); @@ -177,20 +177,20 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn )); $groups = $this->access->fetchListOfGroups($filter, - array($this->access->connection->ldapGroupDisplayName, 'dn')); - if ($groups) { - foreach ($groups as $groupobj) { - $group_dn = $groupobj['dn']; - $all_groups[$group_dn] = $groupobj; - if ($this->access->connection->ldapNestedGroups) { - $supergroups = $this->_getGroupsByMember($group_dn, $seen); - if ($supergroups) { - $all_groups = array_merge($all_groups, $supergroups); - } - } - } - } - return $all_groups; + array($this->access->connection->ldapGroupDisplayName, 'dn')); + if ($groups) { + foreach ($groups as $groupobj) { + $groupDN = $groupobj['dn']; + $allGroups[$groupDN] = $groupobj; + if ($this->access->connection->ldapNestedGroups) { + $supergroups = $this->_getGroupsByMember($groupDN, $seen); + if ($supergroups) { + $allGroups = array_merge($allGroups, $supergroups); + } + } + } + } + return $allGroups; } /** From d10f6e94dcc92a2242aae2b2331b6f85da8d8b6c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 12 Feb 2014 16:56:17 +0100 Subject: [PATCH 6/7] fix coding style to blizzz happy.. ;-) --- apps/user_ldap/group_ldap.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index fb47de7d94..509f65712f 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -105,10 +105,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $seen[$dnGroup] = 1; $members = $this->access->readAttribute($dnGroup, $this->access->connection->ldapGroupMemberAssocAttr, $this->access->connection->ldapGroupFilter); - if ($members) { + if (is_array($members)) { foreach ($members as $memberDN) { $allMembers[$memberDN] = 1; - if ($this->access->connection->ldapNestedGroups) { + $nestedGroups = $this->access->connection->ldapNestedGroups; + if (!empty($nestedGroups)) { $subMembers = $this->_groupMembers($memberDN, $seen); if ($subMembers) { $allMembers = array_merge($allMembers, $subMembers); @@ -155,14 +156,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $uid = $userDN; } - $groups = array_values($this->_getGroupsByMember($uid)); + $groups = array_values($this->getGroupsByMember($uid)); $groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING); $this->access->connection->writeToCache($cacheKey, $groups); return $groups; } - private function _getGroupsByMember($dn, &$seen = null) { + private function getGroupsByMember($dn, &$seen = null) { if ($seen === null) { $seen = array(); } @@ -178,13 +179,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { )); $groups = $this->access->fetchListOfGroups($filter, array($this->access->connection->ldapGroupDisplayName, 'dn')); - if ($groups) { + if (is_array($groups)) { foreach ($groups as $groupobj) { $groupDN = $groupobj['dn']; $allGroups[$groupDN] = $groupobj; - if ($this->access->connection->ldapNestedGroups) { - $supergroups = $this->_getGroupsByMember($groupDN, $seen); - if ($supergroups) { + $nestedGroups = $this->access->connection->ldapNestedGroups; + if (!empty($nestedGroups)) { + $supergroups = $this->getGroupsByMember($groupDN, $seen); + if (is_array($supergroups) && (count($supergroups)>0)) { $allGroups = array_merge($allGroups, $supergroups); } } From 300b1131b47842252395d54ad28b4d6691d7775e Mon Sep 17 00:00:00 2001 From: root Date: Fri, 21 Feb 2014 17:09:36 +0100 Subject: [PATCH 7/7] replace spaces with tabs use true instead of 1 --- apps/user_ldap/group_ldap.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 509f65712f..cef9ca3c4c 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -94,8 +94,8 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } $allMembers = array(); if (array_key_exists($dnGroup, $seen)) { - // avoid loops - return array(); + // avoid loops + return array(); } // used extensively in cron job, caching makes sense for nested groups $cacheKey = '_groupMembers'.$dnGroup; @@ -107,7 +107,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $this->access->connection->ldapGroupFilter); if (is_array($members)) { foreach ($members as $memberDN) { - $allMembers[$memberDN] = 1; + $allMembers[$memberDN] = 1; $nestedGroups = $this->access->connection->ldapNestedGroups; if (!empty($nestedGroups)) { $subMembers = $this->_groupMembers($memberDN, $seen); @@ -117,7 +117,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } } } - $this->access->connection->writeToCache($cacheKey, $allMembers); + $this->access->connection->writeToCache($cacheKey, $allMembers); return $allMembers; } @@ -172,7 +172,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { // avoid loops return array(); } - $seen[$dn] = 1; + $seen[$dn] = true; $filter = $this->access->combineFilterWithAnd(array( $this->access->connection->ldapGroupFilter, $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn