From cb91152dd2a03186cd530f182751e4eb95b8cb8d Mon Sep 17 00:00:00 2001 From: Tobias Perschon Date: Wed, 20 Mar 2019 19:02:25 +0100 Subject: [PATCH] added "zimbraMailForwardingAddress" as a Group-Member association attribute to enable the use of Zimbra Distribution Lists as groups in nextcloud when connecting to a zimbra LDAP Signed-off-by: Tobias Perschon --- apps/user_ldap/lib/Group_LDAP.php | 177 ++++++++++++++++---------- apps/user_ldap/lib/Wizard.php | 2 +- apps/user_ldap/templates/settings.php | 3 +- 3 files changed, 112 insertions(+), 70 deletions(-) diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php index 1658807c0dd96..ed5f241d5a715 100644 --- a/apps/user_ldap/lib/Group_LDAP.php +++ b/apps/user_ldap/lib/Group_LDAP.php @@ -129,37 +129,52 @@ public function inGroup($uid, $gid) { //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. $members = $this->_groupMembers($groupDN); - $members = array_keys($members); // uids are returned as keys + + if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'zimbramailforwardingaddress' + && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid' ){ + + $members = array_keys($members); + // DNs are returned as keys; todo: this is probably only the case if + // nested groups are used and group member attributes are DNs - needs fixing + } + if(!is_array($members) || count($members) === 0) { $this->access->connection->writeToCache($cacheKey, false); return false; } //extra work if we don't get back user DNs - if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') { - $dns = array(); - $filterParts = array(); - $bytes = 0; - foreach($members as $mid) { - $filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter); - $filterParts[] = $filter; - $bytes += strlen($filter); - if($bytes >= 9000000) { - // AD has a default input buffer of 10 MB, we do not want - // to take even the chance to exceed it - $filter = $this->access->combineFilterWithOr($filterParts); - $bytes = 0; - $filterParts = array(); - $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts)); - $dns = array_merge($dns, $users); - } - } - if(count($filterParts) > 0) { - $filter = $this->access->combineFilterWithOr($filterParts); - $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts)); - $dns = array_merge($dns, $users); - } - $members = $dns; + switch(strtolower($this->access->connection->ldapGroupMemberAssocAttr)){ + case "memberuid": + case "zimbramailforwardingaddress": + $dns = array(); + $filterParts = array(); + $bytes = 0; + foreach($members as $mid) { + if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'zimbramailforwardingaddress'){ + $parts = explode("@", $mid); //making sure we get only the uid + $mid = $parts[0]; + } + $filter = str_replace('%uid', $mid, $this->access->connection->ldapLoginFilter); + $filterParts[] = $filter; + $bytes += strlen($filter); + if($bytes >= 9000000) { + // AD has a default input buffer of 10 MB, we do not want + // to take even the chance to exceed it + $filter = $this->access->combineFilterWithOr($filterParts); + $bytes = 0; + $filterParts = array(); + $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts)); + $dns = array_merge($dns, $users); + } + } + if(count($filterParts) > 0) { + $filter = $this->access->combineFilterWithOr($filterParts); + $users = $this->access->fetchListOfUsers($filter, 'dn', count($filterParts)); + $dns = array_merge($dns, $users); + } + $members = $dns; + break; } $isInGroup = in_array($userDN, $members); @@ -706,6 +721,7 @@ public function getUserGroups($uid) { if((int)$this->access->connection->hasMemberOfFilterSupport === 1 && (int)$this->access->connection->useMemberOfToDetectMembership === 1 && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid' + && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'zimbramailforwardingaddress' ) { $groupDNs = $this->_getGroupDNsFromMemberOf($userDN); if (is_array($groupDNs)) { @@ -730,21 +746,27 @@ public function getUserGroups($uid) { } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'uniquemember') - || (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'member') - ) { - $uid = $userDN; - } else if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') { - $result = $this->access->readAttribute($userDN, 'uid'); - if ($result === false) { - \OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '. - $this->access->connection->ldapHost, ILogger::DEBUG); - } - $uid = $result[0]; - } else { - // just in case - $uid = $userDN; - } + switch (strtolower($this->access->connection->ldapGroupMemberAssocAttr)){ + case "uniquemember": + case "member": + $uid = $userDN; + break; + + case "memberuid": + case "zimbramailforwardingaddress": + $result = $this->access->readAttribute($userDN, 'uid'); + if ($result === false) { + \OCP\Util::writeLog('user_ldap', 'No uid attribute found for DN ' . $userDN . ' on '. + $this->access->connection->ldapHost, ILogger::DEBUG); + } + $uid = $result[0]; + break; + + default: + // just in case + $uid = $userDN; + break; + } if(isset($this->cachedGroupsByMember[$uid])) { $groups = array_merge($groups, $this->cachedGroupsByMember[$uid]); @@ -784,6 +806,11 @@ private function getGroupsByMember($dn, &$seen = null) { $allGroups = []; $seen[$dn] = true; $filter = $this->access->connection->ldapGroupMemberAssocAttr.'='.$dn; + + if (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === "zimbramailforwardingaddress") + //in this case the member entries are email addresses + $filter .= '@*'; + $groups = $this->access->fetchListOfGroups($filter, [$this->access->connection->ldapGroupDisplayName, 'dn']); if (is_array($groups)) { @@ -851,34 +878,43 @@ public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { } $groupUsers = array(); - $isMemberUid = (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid'); $attrs = $this->access->userManager->getAttributes(true); foreach($members as $member) { - if($isMemberUid) { - //we got uids, need to get their DNs to 'translate' them to user names - $filter = $this->access->combineFilterWithAnd(array( - str_replace('%uid', trim($member), $this->access->connection->ldapLoginFilter), - $this->access->getFilterPartForUserSearch($search) - )); - $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1); - if(count($ldap_users) < 1) { - continue; - } - $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]); - } else { - //we got DNs, check if we need to filter by search or we can give back all of them - if ($search !== '') { - 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; - } - } + switch (strtolower($this->access->connection->ldapGroupMemberAssocAttr)){ + case "zimbramailforwardingaddress": + //we get email addresses and need to convert them to uids + $parts = explode("@", $member); + $member = $parts[0]; + //no break needed because we just needed to remove the email part and now we have uids + case "memberuid": + //we got uids, need to get their DNs to 'translate' them to user names + $filter = $this->access->combineFilterWithAnd(array( + str_replace('%uid', trim($member), $this->access->connection->ldapLoginFilter), + $this->access->getFilterPartForUserSearch($search) + )); + $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1); + if(count($ldap_users) < 1) { + continue; + } + $groupUsers[] = $this->access->dn2username($ldap_users[0]['dn'][0]); + break; + + + default: + //we got DNs, check if we need to filter by search or we can give back all of them + if ($search !== '') { + 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; + } + break; + } } $groupUsers = array_unique(array_merge($groupUsers, $primaryUsers, $posixGroupUsers)); @@ -933,8 +969,8 @@ public function countUsersInGroup($gid, $search = '') { } $search = $this->access->escapeFilterPart($search, true); $isMemberUid = - (strtolower($this->access->connection->ldapGroupMemberAssocAttr) - === 'memberuid'); + (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid' || + strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'zimbramailforwardingaddress'); //we need to apply the search filter //alternatives that need to be checked: @@ -947,6 +983,11 @@ public function countUsersInGroup($gid, $search = '') { $groupUsers = array(); foreach($members as $member) { if($isMemberUid) { + if(strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'zimbramailforwardingaddress'){ + //we get email addresses and need to convert them to uids + $parts = explode("@", $member); + $member = $parts[0]; + } //we got uids, need to get their DNs to 'translate' them to user names $filter = $this->access->combineFilterWithAnd(array( str_replace('%uid', $member, $this->access->connection->ldapLoginFilter), diff --git a/apps/user_ldap/lib/Wizard.php b/apps/user_ldap/lib/Wizard.php index 26b5a8cab71ca..12de9f1f8c498 100644 --- a/apps/user_ldap/lib/Wizard.php +++ b/apps/user_ldap/lib/Wizard.php @@ -794,7 +794,7 @@ private function checkHost() { * @throws \Exception */ private function detectGroupMemberAssoc() { - $possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'gidNumber'); + $possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'gidNumber', 'zimbraMailForwardingAddress'); $filter = $this->configuration->ldapGroupFilter; if(empty($filter)) { return false; diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index ab7a55f33e477..8a95973245f66 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -95,7 +95,8 @@

-

+

+

t('(New password is sent as plain text to LDAP)'));?>