diff options
Diffstat (limited to 'apps/user_ldap/group_ldap.php')
-rw-r--r-- | apps/user_ldap/group_ldap.php | 235 |
1 files changed, 198 insertions, 37 deletions
diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index 1a35691be85..0d3a70575ba 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -50,20 +50,29 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { if(!$this->enabled) { return false; } - if($this->access->connection->isCached('inGroup'.$uid.':'.$gid)) { - return $this->access->connection->getFromCache('inGroup'.$uid.':'.$gid); + $cacheKey = 'inGroup'.$uid.':'.$gid; + if($this->access->connection->isCached($cacheKey)) { + return $this->access->connection->getFromCache($cacheKey); } - $dn_user = $this->access->username2dn($uid); - $dn_group = $this->access->groupname2dn($gid); + + $userDN = $this->access->username2dn($uid); + $groupDN = $this->access->groupname2dn($gid); // just in case - if(!$dn_group || !$dn_user) { - $this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false); + if(!$groupDN || !$userDN) { + $this->access->connection->writeToCache($cacheKey, false); return false; } + + //check primary group first + if($gid === $this->getUserPrimaryGroup($userDN)) { + $this->access->connection->writeToCache($cacheKey, true); + return true; + } + //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = array_keys($this->_groupMembers($dn_group)); + $members = array_keys($this->_groupMembers($groupDN)); if(!$members) { - $this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, false); + $this->access->connection->writeToCache($cacheKey, false); return false; } @@ -82,8 +91,8 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $members = $dns; } - $isInGroup = in_array($dn_user, $members); - $this->access->connection->writeToCache('inGroup'.$uid.':'.$gid, $isInGroup); + $isInGroup = in_array($userDN, $members); + $this->access->connection->writeToCache($cacheKey, $isInGroup); return $isInGroup; } @@ -91,6 +100,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { /** * @param string $dnGroup * @param array|null &$seen + * @return array|mixed|null */ private function _groupMembers($dnGroup, &$seen = null) { if ($seen === null) { @@ -126,6 +136,125 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } /** + * translates a primary group ID into an ownCloud internal name + * @param string $gid as given by primaryGroupID on AD + * @param string $dn a DN that belongs to the same domain as the group + * @return string|bool + */ + public function primaryGroupID2Name($gid, $dn) { + $cacheKey = 'primaryGroupIDtoName'; + if($this->access->connection->isCached($cacheKey)) { + $groupNames = $this->access->connection->getFromCache($cacheKey); + if(isset($groupNames[$gid])) { + return $groupNames[$gid]; + } + } + + $domainObjectSid = $this->access->getSID($dn); + if($domainObjectSid === false) { + return false; + } + + //we need to get the DN from LDAP + $filter = $this->access->combineFilterWithAnd(array( + $this->access->connection->ldapGroupFilter, + 'objectsid=' . $domainObjectSid . '-' . $gid + )); + $result = $this->access->searchGroups($filter, array('dn'), 1); + if(empty($result)) { + return false; + } + $dn = $result[0]; + + //and now the group name + //NOTE once we have separate ownCloud group IDs and group names we can + //directly read the display name attribute instead of the DN + $name = $this->access->dn2groupname($dn); + + $this->access->connection->writeToCache($cacheKey, $name); + + return $name; + } + + /** + * returns the entry's primary group ID + * @param string $dn + * @param string $attribute + * @return string|bool + */ + private function getEntryGroupID($dn, $attribute) { + $value = $this->access->readAttribute($dn, $attribute); + if(is_array($value) && !empty($value)) { + return $value[0]; + } + return false; + } + + /** + * returns the group's primary ID + * @param string $dn + * @return string|bool + */ + public function getGroupPrimaryGroupID($dn) { + return $this->getEntryGroupID($dn, 'primaryGroupToken'); + } + + /** + * returns the user's primary group ID + * @param string $dn + * @return string|bool + */ + public function getUserPrimaryGroupIDs($dn) { + return $this->getEntryGroupID($dn, 'primaryGroupID'); + } + + /** + * returns a list of users that have the given group as primary group + * + * @param string $groupDN + * @param $limit + * @param int $offset + * @return string[] + */ + public function getUsersInPrimaryGroup($groupDN, $limit = -1, $offset = 0) { + $groupID = $this->getGroupPrimaryGroupID($groupDN); + if($groupID === false) { + return array(); + } + + $filter = $this->access->combineFilterWithAnd(array( + $this->access->connection->ldapUserFilter, + 'primaryGroupID=' . $groupID + )); + + $users = $this->access->fetchListOfUsers( + $filter, + array($this->access->connection->ldapUserDisplayName, 'dn'), + $limit, + $offset + ); + + return $users; + } + + /** + * gets the primary group of a user + * @param string $dn + * @return string + */ + public function getUserPrimaryGroup($dn) { + $groupID = $this->getUserPrimaryGroupIDs($dn); + if($groupID !== false) { + $groupName = $this->primaryGroupID2Name($groupID, $dn); + if($groupName !== false) { + return $groupName; + } + } + + return false; + } + + /** * Get all groups a user belongs to * @param string $uid Name of the user * @return array with group names @@ -161,7 +290,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } $groups = array_values($this->getGroupsByMember($uid)); - $groups = array_unique($this->access->ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = $this->access->ownCloudGroupNames($groups); + + $primaryGroup = $this->getUserPrimaryGroup($userDN); + if($primaryGroup !== false) { + $groups[] = $primaryGroup; + } + + $groups = array_unique($groups, SORT_LOCALE_STRING); $this->access->connection->writeToCache($cacheKey, $groups); return $groups; @@ -170,6 +306,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { /** * @param string $dn * @param array|null &$seen + * @return array */ private function getGroupsByMember($dn, &$seen = null) { if ($seen === null) { @@ -205,6 +342,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { /** * get a list of all users in a group + * + * @param string $gid + * @param string $search + * @param int $limit + * @param int $offset * @return array with user ids */ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { @@ -214,9 +356,9 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { if(!$this->groupExists($gid)) { return array(); } - $cachekey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset; + $cacheKey = 'usersInGroup-'.$gid.'-'.$search.'-'.$limit.'-'.$offset; // check for cache of the exact query - $groupUsers = $this->access->connection->getFromCache($cachekey); + $groupUsers = $this->access->connection->getFromCache($cacheKey); if(!is_null($groupUsers)) { return $groupUsers; } @@ -225,7 +367,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $groupUsers = $this->access->connection->getFromCache('usersInGroup-'.$gid.'-'.$search); if(!is_null($groupUsers)) { $groupUsers = array_slice($groupUsers, $offset, $limit); - $this->access->connection->writeToCache($cachekey, $groupUsers); + $this->access->connection->writeToCache($cacheKey, $groupUsers); return $groupUsers; } @@ -235,14 +377,14 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $groupDN = $this->access->groupname2dn($gid); if(!$groupDN) { // group couldn't be found, return empty resultset - $this->access->connection->writeToCache($cachekey, array()); + $this->access->connection->writeToCache($cacheKey, array()); return array(); } $members = array_keys($this->_groupMembers($groupDN)); if(!$members) { - //in case users could not be retrieved, return empty resultset - $this->access->connection->writeToCache($cachekey, array()); + //in case users could not be retrieved, return empty result set + $this->access->connection->writeToCache($cacheKey, array()); return array(); } @@ -250,7 +392,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid'); foreach($members as $member) { if($isMemberUid) { - //we got uids, need to get their DNs to 'tranlsate' them to usernames + //we got uids, need to get their DNs to 'translate' them to user names $filter = $this->access->combineFilterWithAnd(array( \OCP\Util::mb_str_replace('%uid', $member, $this->access->connection->ldapLoginFilter, 'UTF-8'), @@ -276,10 +418,16 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } } } + natsort($groupUsers); $this->access->connection->writeToCache('usersInGroup-'.$gid.'-'.$search, $groupUsers); $groupUsers = array_slice($groupUsers, $offset, $limit); - $this->access->connection->writeToCache($cachekey, $groupUsers); + + //and get users that have the group as primary + $primaryUsers = $this->getUsersInPrimaryGroup($groupDN, $limit, $offset); + $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers)); + + $this->access->connection->writeToCache($cacheKey, $groupUsers); return $groupUsers; } @@ -291,32 +439,32 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { * @return int|bool */ public function countUsersInGroup($gid, $search = '') { - $cachekey = 'countUsersInGroup-'.$gid.'-'.$search; + $cacheKey = 'countUsersInGroup-'.$gid.'-'.$search; if(!$this->enabled || !$this->groupExists($gid)) { return false; } - $groupUsers = $this->access->connection->getFromCache($cachekey); + $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); + // group couldn't be found, return empty result set + $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); + //in case users could not be retrieved, return empty result set + $this->access->connection->writeToCache($cacheKey, false); return false; } if(empty($search)) { $groupUsers = count($members); - $this->access->connection->writeToCache($cachekey, $groupUsers); + $this->access->connection->writeToCache($cacheKey, $groupUsers); return $groupUsers; } $isMemberUid = @@ -334,7 +482,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $groupUsers = array(); foreach($members as $member) { if($isMemberUid) { - //we got uids, need to get their DNs to 'tranlsate' them to usernames + //we got uids, need to get their DNs to 'translate' them to user names $filter = $this->access->combineFilterWithAnd(array( \OCP\Util::mb_str_replace('%uid', $member, $this->access->connection->ldapLoginFilter, 'UTF-8'), @@ -359,11 +507,19 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { } } + //and get users that have the group as primary + $primaryUsers = $this->getUsersInPrimaryGroup($groupDN); + $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers)); + return count($groupUsers); } /** * get a list of all groups + * + * @param string $search + * @param $limit + * @param int $offset * @return array with group names * * Returns a list with all groups (used by getGroups) @@ -372,11 +528,11 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { if(!$this->enabled) { return array(); } - $cachekey = 'getGroups-'.$search.'-'.$limit.'-'.$offset; + $cacheKey = 'getGroups-'.$search.'-'.$limit.'-'.$offset; //Check cache before driving unnecessary searches - \OCP\Util::writeLog('user_ldap', 'getGroups '.$cachekey, \OCP\Util::DEBUG); - $ldap_groups = $this->access->connection->getFromCache($cachekey); + \OCP\Util::writeLog('user_ldap', 'getGroups '.$cacheKey, \OCP\Util::DEBUG); + $ldap_groups = $this->access->connection->getFromCache($cacheKey); if(!is_null($ldap_groups)) { return $ldap_groups; } @@ -397,26 +553,30 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $offset); $ldap_groups = $this->access->ownCloudGroupNames($ldap_groups); - $this->access->connection->writeToCache($cachekey, $ldap_groups); + $this->access->connection->writeToCache($cacheKey, $ldap_groups); return $ldap_groups; } /** * get a list of all groups using a paged search + * + * @param string $search + * @param int $limit + * @param int $offset * @return array with group names * * Returns a list with all groups - * Uses a paged search if available to override a - * server side search limit. - * (active directory has a limit of 1000 by default) + * Uses a paged search if available to override a + * server side search limit. + * (active directory has a limit of 1000 by default) */ public function getGroups($search = '', $limit = -1, $offset = 0) { if(!$this->enabled) { return array(); } - $pagingsize = $this->access->connection->ldapPagingSize; + $pagingSize = $this->access->connection->ldapPagingSize; if ((! $this->access->connection->hasPagedResultSupport) - || empty($pagingsize)) { + || empty($pagingSize)) { return $this->getGroupsChunk($search, $limit, $offset); } $maxGroups = 100000; // limit max results (just for safety reasons) @@ -428,7 +588,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { $chunkOffset = $offset; $allGroups = array(); while ($chunkOffset < $overallLimit) { - $chunkLimit = min($pagingsize, $overallLimit - $chunkOffset); + $chunkLimit = min($pagingSize, $overallLimit - $chunkOffset); $ldapGroups = $this->getGroupsChunk($search, $chunkLimit, $chunkOffset); $nread = count($ldapGroups); \OCP\Util::writeLog('user_ldap', 'getGroups('.$search.'): read '.$nread.' at offset '.$chunkOffset.' (limit: '.$chunkLimit.')', \OCP\Util::DEBUG); @@ -445,6 +605,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface { /** * @param string $group + * @return bool */ public function groupMatchesFilter($group) { return (strripos($group, $this->groupSearch) !== false); |