* ActiveDirectory: fix account-selection type groupmembers (applies only to non-admins!) and ignore Domain Users group

not setting caseIgnoreMatch for sorting, as its not supported for Windows ActiveDirectory
This commit is contained in:
Ralf Becker 2020-09-08 15:29:51 +02:00
parent 5afe7ddbca
commit 9f9cce88b2
5 changed files with 80 additions and 15 deletions

View File

@ -246,6 +246,7 @@ class Accounts
$members = array();
foreach((array)$this->memberships($GLOBALS['egw_info']['user']['account_id'],true) as $grp)
{
if (isset($this->backend->ignore_membership) && in_array($grp, $this->backend->ignore_membership)) continue;
$members = array_unique(array_merge($members, (array)$this->members($grp,true,$param['active'])));
if ($param['type'] == 'groupmembers+memberships') $members[] = $grp;
}
@ -313,6 +314,7 @@ class Accounts
* @param array $options
* $options['filter']['group'] only return members of that group
* $options['account_type'] "accounts", "groups", "both" or "groupmembers"
* $options['tag_list'] true: return array of values for keys "value", "label" and "icon"
* @return array with id - title pairs of the matching entries
*/
public static function link_query($pattern, array &$options = array())
@ -367,8 +369,24 @@ class Accounts
'order' => $order,
)) as $account)
{
$accounts[$account['account_id']] = self::format_username($account['account_lid'],
$displayName = self::format_username($account['account_lid'],
$account['account_firstname'],$account['account_lastname'],$account['account_id']);
if (!empty($options['tag_list']))
{
$accounts[] = [
'value' => $account['account_id'],
'label' => $displayName,
'icon' => Framework::link('/api/avatar.php', [
'account_id' => $account['account_id'],
'modified' => $account['account_modified'],
]),
];
}
else
{
$accounts[$account['account_id']] = $displayName;
}
}
return $accounts;
}

View File

@ -109,7 +109,14 @@ class Ads
*/
const MIN_ACCOUNT_ID = 1000;
/**
* Ignore group-membership of following groups, when compiling group-members
*
* We ignore "Domain Users" group with RID 513, as it contains all users!
*
* @var int[]
*/
public $ignore_membership = [ -513 ];
/**
* Timestamps ldap => egw used in several places
*
@ -1081,6 +1088,20 @@ class Ads
{
$type_filter .= $this->frontend->config['ads_user_filter'];
}
// for non-admins and account_selection "groupmembers" we have to filter by memberOf attribute
if ($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] === 'groupmembers' &&
(!isset($GLOBALS['egw_info']['user']['apps']['admin'])))
{
$type_filter .= '(|';
foreach($GLOBALS['egw']->accounts->memberships($GLOBALS['egw_info']['user']['account_id'],true) as $group_id)
{
if (!in_array($group_id, $this->ignore_membership) && ($dn = Api\Accounts::id2name($group_id, 'account_dn')))
{
$type_filter .= '(memberOf='.$dn.')(primaryGroupID='.abs($group_id).')';
}
}
$type_filter .= ')';
}
$type_filter .= ')';
if ($account_type === 'u') break;
$user_filter = $type_filter;
@ -1103,9 +1124,11 @@ class Ads
/**
* Get value(s) for LDAP_CONTROL_SORTREQUEST
*
* Sorting by multiple criteria is supported in LDAP RFC 2891, but - at least with Univention Samba - gives wired results,
* Windows AD does NOT support it and gives an error if the oid is specified!
*
* @param ?string $order_by sql order string eg. "contact_email ASC"
* @return array of arrays with values for keys 'attr', 'oid' (caseIgnoreMatch='2.5.13.3') and 'reverse'
* @todo sorting by multiple criteria is supported in LDAP RFC 2891, but - at least with Univention - gives wired results
*/
protected function sort_values($order_by)
{
@ -1122,14 +1145,17 @@ class Ads
}
elseif (($attr = array_search('account_'.$matches[2], $this->attributes2egw)))
{
$values[] = [
'attr' => $attr,
$value = [
'attr' => $mapping[$matches[2]],
'oid' => '2.5.13.3', // caseIgnoreMatch
'reverse' => strtoupper($matches[3]) === ' DESC',
];
// Windows AD does NOT support caseIgnoreMatch sorting, only it's default sorting
if ($this->serverinfo->activeDirectory(true)) unset($value['oid']);
$values[] = $value;
}
$order_by = substr($order_by, strlen($matches[0]));
if ($values) break; // sorting by multiple criteria gives wired results
if ($values) break; // sorting by multiple criteria gives no result for Windows AD and wired result for Samba4
}
return $values;
}

View File

@ -233,7 +233,7 @@ class Ads extends Ldap
$filter = '';
if ($gid < 0 && ($dn = $GLOBALS['egw']->accounts->id2name($gid, 'account_dn')))
{
$filter .= '(memberOf='.$dn.')';
$filter .= '(|(memberOf='.$dn.')(primaryGroupID='.abs($gid).'))';
}
return $filter;
}

View File

@ -1096,9 +1096,11 @@ class Ldap
/**
* Get value(s) for LDAP_CONTROL_SORTREQUEST
*
* Sorting by multiple criteria is supported in LDAP RFC 2891, but - at least with Univention Samba - gives wired results,
* Windows AD does NOT support it and gives an error if the oid is specified!
*
* @param ?string $order_by sql order string eg. "contact_email ASC"
* @return array of arrays with values for keys 'attr', 'oid' (caseIgnoreMatch='2.5.13.3') and 'reverse'
* @todo sorting by multiple criteria is supported in LDAP RFC 2891, but - at least with Univention - gives wired results
*/
protected function sort_values($order_by)
{
@ -1119,17 +1121,20 @@ class Ldap
{
if (isset($mapping[$matches[2]]))
{
$values[] = [
$value = [
'attr' => $mapping[$matches[2]],
'oid' => '2.5.13.3', // caseIgnoreMatch
'reverse' => strtoupper($matches[3]) === ' DESC',
];
// Windows AD does NOT support caseIgnoreMatch sorting, only it's default sorting
if ($this->ldapServerInfo->activeDirectory(true)) unset($value['oid']);
$values[] = $value;
break;
}
}
}
$order_by = substr($order_by, strlen($matches[0]));
if ($values) break; // sorting by multiple criteria gives wired results
if ($values) break; // sorting by multiple criteria gives no result for Windows AD and wired result for Samba4
}
//error_log(__METHOD__."('$order_by') returning ".json_encode($values));
return $values;

View File

@ -26,9 +26,13 @@ class ServerInfo
*/
const OPENLDAP = 1;
/**
* Samba4 LDAP server
* Samba4 Active Directory server
*/
const SAMBA4 = 2;
/**
* Windows Active Directory server
*/
const WINDOWS_AD = 4;
/**
* @var array $namingContext holds the supported namingcontexts
@ -112,6 +116,16 @@ class ServerInfo
$this->serverType = $_serverType;
}
/**
* @param ?bool $windows_ad true: check for windows AD, false: check for Samba4, null: check of any AD
* @return bool
*/
function activeDirectory($windows_ad=null)
{
return !isset($windows_ad) ? in_array($this->serverType, [self::WINDOWS_AD, self::SAMBA4], true) :
$this->serverType === ($windows_ad ? self::WINDOWS_AD : self::SAMBA4);
}
/**
* sets the DN for the subschema entry
*
@ -190,7 +204,7 @@ class ServerInfo
public static function get($ds, $host, $version=3)
{
$filter='(objectclass=*)';
$justthese = array('structuralObjectClass','namingContexts','supportedLDAPVersion','subschemaSubentry','vendorname','supportedControl');
$justthese = array('structuralObjectClass','namingContexts','supportedLDAPVersion','subschemaSubentry','vendorname','supportedControl','forestFunctionality');
if(($sr = @ldap_read($ds, '', $filter, $justthese)))
{
if(($info = ldap_get_entries($ds, $sr)))
@ -222,9 +236,11 @@ class ServerInfo
}
$ldapServerInfo->setServerType($ldapServerType);
}
if ($info[0]['vendorname'] && stripos($info[0]['vendorname'][0], 'samba') !== false)
// Check for ActiveDirectory by forestFunctionality and set Samba4 if vendorName includes samba
if(!empty($info[0]['forestfunctionality'][0]))
{
$ldapServerInfo->setServerType(self::SAMBA4);
$ldapServerInfo->setServerType(!empty($info[0]['vendorname']) && stripos($info[0]['vendorname'][0], 'samba') !== false ?
self::SAMBA4 : self::WINDOWS_AD);
}
// check for subschema entry dn