From 551d904bb077df9cbc6871e54a0445b06484debb Mon Sep 17 00:00:00 2001
From: Tobias Perschon
Date: Tue, 13 Aug 2019 23:35:34 +0200
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
fix cs:check
Signed-off-by: Tobias Perschon
Update apps/user_ldap/lib/Group_LDAP.php
Co-authored-by: blizzz
Signed-off-by: Tobias Perschon
---
apps/user_ldap/lib/Group_LDAP.php | 209 ++++++++++++++++----------
apps/user_ldap/lib/Wizard.php | 2 +-
apps/user_ldap/templates/settings.php | 5 +-
3 files changed, 131 insertions(+), 85 deletions(-)
diff --git a/apps/user_ldap/lib/Group_LDAP.php b/apps/user_ldap/lib/Group_LDAP.php
index 6f6e7362e97..199865e7623 100644
--- a/apps/user_ldap/lib/Group_LDAP.php
+++ b/apps/user_ldap/lib/Group_LDAP.php
@@ -20,6 +20,7 @@
* @author Roeland Jago Douma
* @author Roland Tapken
* @author Thomas Müller
+ * @author Tobias Perschon
* @author Victor Dubiniuk
* @author Vincent Petry
* @author Vinicius Cubas Brand
@@ -66,6 +67,11 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
/** @var ILogger */
protected $logger;
+ /**
+ * @var string $ldapGroupMemberAssocAttr contains the LDAP setting (in lower case) with the same name
+ */
+ protected $ldapGroupMemberAssocAttr;
+
public function __construct(Access $access, GroupPluginManager $groupPluginManager) {
parent::__construct($access);
$filter = $this->access->connection->ldapGroupFilter;
@@ -79,6 +85,7 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
$this->cachedNestedGroups = new CappedMemoryCache();
$this->groupPluginManager = $groupPluginManager;
$this->logger = OC::$server->getLogger();
+ $this->ldapGroupMemberAssocAttr = strtolower($gAssoc);
}
/**
@@ -136,31 +143,38 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
//extra work if we don't get back user DNs
- if (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
- $requestAttributes = $this->access->userManager->getAttributes(true);
- $dns = [];
- $filterParts = [];
- $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
+ switch ($this->ldapGroupMemberAssocAttr) {
+ case 'memberuid':
+ case 'zimbramailforwardingaddress':
+ $requestAttributes = $this->access->userManager->getAttributes(true);
+ $dns = [];
+ $filterParts = [];
+ $bytes = 0;
+ foreach ($members as $mid) {
+ if ($this->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);
+ $users = $this->access->fetchListOfUsers($filter, $requestAttributes, count($filterParts));
+ $bytes = 0;
+ $filterParts = [];
+ $dns = array_merge($dns, $users);
+ }
+ }
+ if (count($filterParts) > 0) {
$filter = $this->access->combineFilterWithOr($filterParts);
- $bytes = 0;
- $filterParts = [];
$users = $this->access->fetchListOfUsers($filter, $requestAttributes, count($filterParts));
$dns = array_merge($dns, $users);
}
- }
- if (count($filterParts) > 0) {
- $filter = $this->access->combineFilterWithOr($filterParts);
- $users = $this->access->fetchListOfUsers($filter, $requestAttributes, count($filterParts));
- $dns = array_merge($dns, $users);
- }
- $members = $dns;
+ $members = $dns;
+ break;
}
$isInGroup = in_array($userDN, $members);
@@ -673,8 +687,8 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
// memberof doesn't support memberuid, so skip it here.
if ((int)$this->access->connection->hasMemberOfFilterSupport === 1
&& (int)$this->access->connection->useMemberOfToDetectMembership === 1
- && strtolower($this->access->connection->ldapGroupMemberAssocAttr) !== 'memberuid'
- ) {
+ && $this->ldapGroupMemberAssocAttr !== 'memberuid'
+ && $this->ldapGroupMemberAssocAttr !== 'zimbramailforwardingaddress') {
$groupDNs = $this->_getGroupDNsFromMemberOf($userDN);
if (is_array($groupDNs)) {
foreach ($groupDNs as $dn) {
@@ -698,27 +712,33 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
//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;
- } elseif (strtolower($this->access->connection->ldapGroupMemberAssocAttr) === 'memberuid') {
- $result = $this->access->readAttribute($userDN, 'uid');
- if ($result === false) {
- $this->logger->debug('No uid attribute found for DN {dn} on {host}',
- [
- 'app' => 'user_ldap',
- 'dn' => $userDN,
- 'host' => $this->access->connection->ldapHost,
- ]
- );
- $uid = false;
- } else {
- $uid = $result[0];
- }
- } else {
- // just in case
- $uid = $userDN;
+ switch ($this->ldapGroupMemberAssocAttr) {
+ case 'uniquemember':
+ case 'member':
+ $uid = $userDN;
+ break;
+
+ case 'memberuid':
+ case 'zimbramailforwardingaddress':
+ $result = $this->access->readAttribute($userDN, 'uid');
+ if ($result === false) {
+ $this->logger->debug('No uid attribute found for DN {dn} on {host}',
+ [
+ 'app' => 'user_ldap',
+ 'dn' => $userDN,
+ 'host' => $this->access->connection->ldapHost,
+ ]
+ );
+ $uid = false;
+ } else {
+ $uid = $result[0];
+ }
+ break;
+
+ default:
+ // just in case
+ $uid = $userDN;
+ break;
}
if ($uid !== false) {
@@ -759,6 +779,12 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
$allGroups = [];
$seen[$dn] = true;
$filter = $this->access->connection->ldapGroupMemberAssocAttr . '=' . $dn;
+
+ if ($this->ldapGroupMemberAssocAttr === 'zimbramailforwardingaddress') {
+ //in this case the member entries are email addresses
+ $filter .= '@*';
+ }
+
$groups = $this->access->fetchListOfGroups($filter,
[strtolower($this->access->connection->ldapGroupMemberAssocAttr), $this->access->connection->ldapGroupDisplayName, 'dn']);
if (is_array($groups)) {
@@ -768,6 +794,11 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
return $this->getGroupsByMember($dn, $seen);
};
+
+ if (empty($dn)) {
+ $dn = "";
+ }
+
$allGroups = $this->walkNestedGroups($dn, $fetcher, $groups);
}
$visibleGroups = $this->filterValidGroups($allGroups);
@@ -828,50 +859,57 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
$groupUsers = [];
- $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([
- str_replace('%uid', trim($member), $this->access->connection->ldapLoginFilter),
- $this->access->combineFilterWithAnd([
- $this->access->getFilterPartForUserSearch($search),
- $this->access->connection->ldapUserFilter
- ])
- ]);
- $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
- $uid = $this->access->dn2username($member);
- if (!$uid) {
- continue;
- }
-
- $cacheKey = 'userExistsOnLDAP' . $uid;
- $userExists = $this->access->connection->getFromCache($cacheKey);
- if ($userExists === false) {
- continue;
- }
- if ($userExists === null || $search !== '') {
- if (!$this->access->readAttribute($member,
- $this->access->connection->ldapUserDisplayName,
+ switch ($this->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([
+ str_replace('%uid', trim($member), $this->access->connection->ldapLoginFilter),
$this->access->combineFilterWithAnd([
$this->access->getFilterPartForUserSearch($search),
$this->access->connection->ldapUserFilter
- ]))) {
- if ($search === '') {
- $this->access->connection->writeToCache($cacheKey, false);
- }
+ ])
+ ]);
+ $ldap_users = $this->access->fetchListOfUsers($filter, $attrs, 1);
+ if (count($ldap_users) < 1) {
continue;
}
- $this->access->connection->writeToCache($cacheKey, true);
- }
- $groupUsers[] = $uid;
+ $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
+ $uid = $this->access->dn2username($member);
+ if (!$uid) {
+ continue;
+ }
+
+ $cacheKey = 'userExistsOnLDAP' . $uid;
+ $userExists = $this->access->connection->getFromCache($cacheKey);
+ if ($userExists === false) {
+ continue;
+ }
+ if ($userExists === null || $search !== '') {
+ if (!$this->access->readAttribute($member,
+ $this->access->connection->ldapUserDisplayName,
+ $this->access->combineFilterWithAnd([
+ $this->access->getFilterPartForUserSearch($search),
+ $this->access->connection->ldapUserFilter
+ ]))) {
+ if ($search === '') {
+ $this->access->connection->writeToCache($cacheKey, false);
+ }
+ continue;
+ }
+ $this->access->connection->writeToCache($cacheKey, true);
+ }
+ $groupUsers[] = $uid;
+ break;
}
}
@@ -930,8 +968,8 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
}
$search = $this->access->escapeFilterPart($search, true);
$isMemberUid =
- (strtolower($this->access->connection->ldapGroupMemberAssocAttr)
- === 'memberuid');
+ ($this->ldapGroupMemberAssocAttr === 'memberuid' ||
+ $this->ldapGroupMemberAssocAttr === 'zimbramailforwardingaddress');
//we need to apply the search filter
//alternatives that need to be checked:
@@ -944,6 +982,11 @@ class Group_LDAP extends BackendUtility implements GroupInterface, IGroupLDAP, I
$groupUsers = [];
foreach ($members as $member) {
if ($isMemberUid) {
+ if ($this->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([
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 73032bfd7f2..32ad19efa5b 100644
--- a/apps/user_ldap/lib/Wizard.php
+++ b/apps/user_ldap/lib/Wizard.php
@@ -794,7 +794,7 @@ class Wizard extends LDAPUtility {
* @throws \Exception
*/
private function detectGroupMemberAssoc() {
- $possibleAttrs = ['uniqueMember', 'memberUid', 'member', 'gidNumber'];
+ $possibleAttrs = ['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 6b0221f0935..32dfdd12abe 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -103,7 +103,10 @@ style('user_ldap', 'settings');
p(' selected');
} ?>>member (AD)
+ } ?>>gidNumber
+
t('(New password is sent as plain text to LDAP)'));?>
--
2.39.5