mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-25 09:23:28 +01:00
* LDAP/ActiveDirectory Sync: permanently store DN+entryUUID and use the latter to detect renamed user or accounts
This commit is contained in:
parent
10a7a4bd7e
commit
9888a681e7
@ -11,7 +11,7 @@
|
|||||||
/* Basic information about this app */
|
/* Basic information about this app */
|
||||||
$setup_info['api']['name'] = 'api';
|
$setup_info['api']['name'] = 'api';
|
||||||
$setup_info['api']['title'] = 'EGroupware API';
|
$setup_info['api']['title'] = 'EGroupware API';
|
||||||
$setup_info['api']['version'] = '23.1.007';
|
$setup_info['api']['version'] = '23.1.008';
|
||||||
$setup_info['api']['versions']['current_header'] = '1.29';
|
$setup_info['api']['versions']['current_header'] = '1.29';
|
||||||
// maintenance release in sync with changelog in doc/rpm-build/debian.changes
|
// maintenance release in sync with changelog in doc/rpm-build/debian.changes
|
||||||
$setup_info['api']['versions']['maintenance_release'] = '23.1.20240624';
|
$setup_info['api']['versions']['maintenance_release'] = '23.1.20240624';
|
||||||
|
@ -64,7 +64,9 @@ $phpgw_baseline = array(
|
|||||||
'account_expires' => array('type' => 'int','precision' => '4'),
|
'account_expires' => array('type' => 'int','precision' => '4'),
|
||||||
'account_type' => array('type' => 'char','precision' => '1'),
|
'account_type' => array('type' => 'char','precision' => '1'),
|
||||||
'account_primary_group' => array('type' => 'int','meta' => 'group','precision' => '4','nullable' => False,'default' => '0'),
|
'account_primary_group' => array('type' => 'int','meta' => 'group','precision' => '4','nullable' => False,'default' => '0'),
|
||||||
'account_description' => array('type' => 'varchar','precision' => '255','comment' => 'group description')
|
'account_description' => array('type' => 'varchar','precision' => '255','comment' => 'group description'),
|
||||||
|
'account_uuid' => array('type' => 'ascii','precision' => '64','comment' => 'UUID of synced (LDAP) entries'),
|
||||||
|
'account_dn' => array('type' => 'varchar','precision' => '255','comment' => 'DN or container')
|
||||||
),
|
),
|
||||||
'pk' => array('account_id'),
|
'pk' => array('account_id'),
|
||||||
'fk' => array(),
|
'fk' => array(),
|
||||||
|
@ -977,3 +977,19 @@ function api_upgrade23_1_006()
|
|||||||
|
|
||||||
return $GLOBALS['setup_info']['api']['currentver'] = '23.1.007';
|
return $GLOBALS['setup_info']['api']['currentver'] = '23.1.007';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function api_upgrade23_1_007()
|
||||||
|
{
|
||||||
|
$GLOBALS['egw_setup']->oProc->AddColumn('egw_accounts','account_uuid',array(
|
||||||
|
'type' => 'ascii',
|
||||||
|
'precision' => '64',
|
||||||
|
'comment' => 'UUID of synced (LDAP) entries'
|
||||||
|
));
|
||||||
|
$GLOBALS['egw_setup']->oProc->AddColumn('egw_accounts','account_dn',array(
|
||||||
|
'type' => 'varchar',
|
||||||
|
'precision' => '255',
|
||||||
|
'comment' => 'DN or container'
|
||||||
|
));
|
||||||
|
|
||||||
|
return $GLOBALS['setup_info']['api']['currentver'] = '23.1.008';
|
||||||
|
}
|
@ -113,7 +113,7 @@ class Ads
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $default_attributes = array(
|
protected static $default_attributes = array(
|
||||||
'objectsid', 'samaccounttype', 'samaccountname',
|
'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,7 +122,7 @@ class Ads
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $user_attributes = array(
|
protected static $user_attributes = array(
|
||||||
'objectsid', 'samaccounttype', 'samaccountname',
|
'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid',
|
||||||
'primarygroupid', 'givenname', 'sn', 'mail', 'displayname', 'telephonenumber',
|
'primarygroupid', 'givenname', 'sn', 'mail', 'displayname', 'telephonenumber',
|
||||||
'objectguid', 'useraccountcontrol', 'accountexpires', 'pwdlastset', 'whencreated', 'whenchanged', 'lastlogon',
|
'objectguid', 'useraccountcontrol', 'accountexpires', 'pwdlastset', 'whencreated', 'whenchanged', 'lastlogon',
|
||||||
'jpegphoto',
|
'jpegphoto',
|
||||||
@ -134,7 +134,7 @@ class Ads
|
|||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $group_attributes = array(
|
protected static $group_attributes = array(
|
||||||
'objectsid', 'samaccounttype', 'samaccountname',
|
'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid',
|
||||||
'objectguid', 'mail', 'whencreated', 'whenchanged', 'description',
|
'objectguid', 'mail', 'whencreated', 'whenchanged', 'description',
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -565,6 +565,7 @@ class Ads
|
|||||||
|
|
||||||
$group = array(
|
$group = array(
|
||||||
'account_dn' => $data['dn'],
|
'account_dn' => $data['dn'],
|
||||||
|
'account_uuid' => $data['entryuuid'][0],
|
||||||
'account_id' => $account_id,
|
'account_id' => $account_id,
|
||||||
'account_sid' => $sid,
|
'account_sid' => $sid,
|
||||||
'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]),
|
'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]),
|
||||||
@ -644,6 +645,7 @@ class Ads
|
|||||||
|
|
||||||
$user = array(
|
$user = array(
|
||||||
'account_dn' => $data['dn'],
|
'account_dn' => $data['dn'],
|
||||||
|
'account_uuid' => $data['entryuuid'][0],
|
||||||
'account_id' => $account_id,
|
'account_id' => $account_id,
|
||||||
'account_sid' => $sid,
|
'account_sid' => $sid,
|
||||||
'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]),
|
'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]),
|
||||||
@ -1327,6 +1329,8 @@ class Ads
|
|||||||
'account_fullname' => 'cn',
|
'account_fullname' => 'cn',
|
||||||
'account_sid' => 'objectsid',
|
'account_sid' => 'objectsid',
|
||||||
'account_guid' => 'objectguid',
|
'account_guid' => 'objectguid',
|
||||||
|
'account_uuid' => 'entryuuid',
|
||||||
|
'account_dn' => 'dn',
|
||||||
);
|
);
|
||||||
$ret = false;
|
$ret = false;
|
||||||
if (isset($to_ldap[$which]))
|
if (isset($to_ldap[$which]))
|
||||||
|
@ -325,7 +325,8 @@ class Import
|
|||||||
$hide_binary = ['jpegphoto' => $contact['jpegphoto'] ? bytes($contact['jpegphoto']).' bytes binary data' : null];
|
$hide_binary = ['jpegphoto' => $contact['jpegphoto'] ? bytes($contact['jpegphoto']).' bytes binary data' : null];
|
||||||
$this->logger(++$num.'. User: '.json_encode($hide_binary + $contact + $account, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), 'debug');
|
$this->logger(++$num.'. User: '.json_encode($hide_binary + $contact + $account, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE), 'debug');
|
||||||
// check if account exists in sql
|
// check if account exists in sql
|
||||||
if (!($account_id = $this->accounts_sql->name2id($account['account_lid'])))
|
if (!($account_id = $this->accounts_sql->name2id($account['account_uuid'], 'account_uuid')) &&
|
||||||
|
!($account_id = $this->accounts_sql->name2id($account['account_lid'])))
|
||||||
{
|
{
|
||||||
$sql_account = $account;
|
$sql_account = $account;
|
||||||
// if only users are imported set primary group as configured
|
// if only users are imported set primary group as configured
|
||||||
@ -702,10 +703,14 @@ class Import
|
|||||||
$local_groups = in_array('local', explode('+', $GLOBALS['egw_info']['server']['account_import_type']));
|
$local_groups = in_array('local', explode('+', $GLOBALS['egw_info']['server']['account_import_type']));
|
||||||
|
|
||||||
// query all groups in SQL
|
// query all groups in SQL
|
||||||
$sql_groups = $groups = $set_members = [];
|
$sql_groups = $uuid2account_id = $groups = $set_members = [];
|
||||||
foreach($GLOBALS['egw']->db->select(Sql::TABLE, 'account_id,account_lid', ['account_type' => 'g'], __LINE__, __FILE__) as $row)
|
foreach($GLOBALS['egw']->db->select(Sql::TABLE, 'account_id,account_lid,account_uuid', ['account_type' => 'g'], __LINE__, __FILE__) as $row)
|
||||||
{
|
{
|
||||||
$sql_groups[-$row['account_id']] = self::strtolower($row['account_lid']);
|
$sql_groups[-$row['account_id']] = self::strtolower($row['account_lid']);
|
||||||
|
if (!empty($row['account_uuid']))
|
||||||
|
{
|
||||||
|
$uuid2account_id[$row['account_uuid']] = -$row['account_id'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// fill groups with existing ones, for incremental sync, but only if we have NO local groups, otherwise we need to query them all
|
// fill groups with existing ones, for incremental sync, but only if we have NO local groups, otherwise we need to query them all
|
||||||
$filter = ['type' => 'groups'];
|
$filter = ['type' => 'groups'];
|
||||||
@ -733,7 +738,13 @@ class Import
|
|||||||
}
|
}
|
||||||
$this->logger(++$num.'. Group: '.json_encode($group, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), 'debug');
|
$this->logger(++$num.'. Group: '.json_encode($group, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES), 'debug');
|
||||||
|
|
||||||
if (!($sql_id = array_search(self::strtolower($group['account_lid']), $sql_groups)))
|
$sql_id = null;
|
||||||
|
// found group by uuid
|
||||||
|
if ($uuid2account_id && ($sql_id = $uuid2account_id[$group['account_uuid']] ?? false))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!$sql_id && !($sql_id = array_search(self::strtolower($group['account_lid']), $sql_groups)))
|
||||||
{
|
{
|
||||||
if ($this->accounts_sql->name2id($group['account_lid']) > 0)
|
if ($this->accounts_sql->name2id($group['account_lid']) > 0)
|
||||||
{
|
{
|
||||||
@ -754,7 +765,7 @@ class Import
|
|||||||
}
|
}
|
||||||
if (($sql_id = $group['account_id'] = $this->accounts_sql->save($group, true)) < 0)
|
if (($sql_id = $group['account_id'] = $this->accounts_sql->save($group, true)) < 0)
|
||||||
{
|
{
|
||||||
// run addgroup hook to create eg. home-directory or mail account
|
// run addgroup hook to create e.g. home-directory or mail account
|
||||||
Api\Hooks::process($group+array(
|
Api\Hooks::process($group+array(
|
||||||
'location' => 'addgroup'
|
'location' => 'addgroup'
|
||||||
),False,True); // called for every app now, not only enabled ones)
|
),False,True); // called for every app now, not only enabled ones)
|
||||||
|
@ -535,7 +535,7 @@ class Ldap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!($data = $this->_ldap_search($this->group_context,'(&(objectClass=posixGroup)(gidnumber=' . abs($account_id).'))',
|
if (!($data = $this->_ldap_search($this->group_context,'(&(objectClass=posixGroup)(gidnumber=' . abs($account_id).'))',
|
||||||
array('dn', 'gidnumber', 'cn', 'objectclass', static::MAIL_ATTR, 'memberuid', 'description'))))
|
array('dn', 'gidnumber', 'cn', 'objectclass', static::MAIL_ATTR, 'memberuid', 'description', 'entryuuid'))))
|
||||||
{
|
{
|
||||||
return false; // group not found
|
return false; // group not found
|
||||||
}
|
}
|
||||||
@ -543,6 +543,7 @@ class Ldap
|
|||||||
|
|
||||||
$group += array(
|
$group += array(
|
||||||
'account_dn' => $data['dn'],
|
'account_dn' => $data['dn'],
|
||||||
|
'account_uuid' => $data['entryuuid'][0],
|
||||||
'account_id' => -$data['gidnumber'][0],
|
'account_id' => -$data['gidnumber'][0],
|
||||||
'account_lid' => $data['cn'][0],
|
'account_lid' => $data['cn'][0],
|
||||||
'account_type' => 'g',
|
'account_type' => 'g',
|
||||||
@ -585,7 +586,7 @@ class Ldap
|
|||||||
|
|
||||||
if (!($data = $this->_ldap_search($this->user_context, '(&(objectclass=posixAccount)(uidnumber=' . (int)$account_id.")$account_filter)",
|
if (!($data = $this->_ldap_search($this->user_context, '(&(objectclass=posixAccount)(uidnumber=' . (int)$account_id.")$account_filter)",
|
||||||
array('dn','uidnumber','uid','gidnumber','givenname','sn','cn',static::MAIL_ATTR,'userpassword','telephonenumber',
|
array('dn','uidnumber','uid','gidnumber','givenname','sn','cn',static::MAIL_ATTR,'userpassword','telephonenumber',
|
||||||
'shadowexpire','shadowlastchange','homedirectory','loginshell','createtimestamp','modifytimestamp'))))
|
'shadowexpire','shadowlastchange','homedirectory','loginshell','createtimestamp','modifytimestamp','entryuuid'))))
|
||||||
{
|
{
|
||||||
return false; // user not found
|
return false; // user not found
|
||||||
}
|
}
|
||||||
@ -593,6 +594,7 @@ class Ldap
|
|||||||
$utc_diff = date('Z');
|
$utc_diff = date('Z');
|
||||||
$user = array(
|
$user = array(
|
||||||
'account_dn' => $data['dn'],
|
'account_dn' => $data['dn'],
|
||||||
|
'account_uuid' => $data['entryuuid'][0],
|
||||||
'account_id' => (int)$data['uidnumber'][0],
|
'account_id' => (int)$data['uidnumber'][0],
|
||||||
'account_lid' => $data['uid'][0],
|
'account_lid' => $data['uid'][0],
|
||||||
'account_type' => 'u',
|
'account_type' => 'u',
|
||||||
@ -893,7 +895,7 @@ class Ldap
|
|||||||
{
|
{
|
||||||
$allValues = $this->vlvSortQuery($this->user_context, $filter,
|
$allValues = $this->vlvSortQuery($this->user_context, $filter,
|
||||||
['uid', 'uidNumber', 'givenname', 'sn', static::MAIL_ATTR, 'shadowExpire', 'createtimestamp',
|
['uid', 'uidNumber', 'givenname', 'sn', static::MAIL_ATTR, 'shadowExpire', 'createtimestamp',
|
||||||
'modifytimestamp', 'objectclass', 'gidNumber', 'jpegphoto'],
|
'modifytimestamp', 'objectclass', 'gidNumber', 'jpegphoto', 'entryuuid'],
|
||||||
$order_by, $start, $offset, $totalcount);
|
$order_by, $start, $offset, $totalcount);
|
||||||
|
|
||||||
$utc_diff = date('Z');
|
$utc_diff = date('Z');
|
||||||
@ -904,6 +906,7 @@ class Ldap
|
|||||||
{
|
{
|
||||||
$account = array(
|
$account = array(
|
||||||
'account_dn' => $allVals['dn'],
|
'account_dn' => $allVals['dn'],
|
||||||
|
'account_uuid' => $allVals['entryuuid'][0],
|
||||||
'account_id' => $allVals['uidnumber'][0],
|
'account_id' => $allVals['uidnumber'][0],
|
||||||
'account_lid' => Api\Translation::convert($allVals['uid'][0], 'utf-8'),
|
'account_lid' => Api\Translation::convert($allVals['uid'][0], 'utf-8'),
|
||||||
'account_type' => 'u',
|
'account_type' => 'u',
|
||||||
@ -963,7 +966,7 @@ class Ldap
|
|||||||
$filter .= "(modifytimestamp>=".gmdate('YmdHis', $param['modified']).".0Z)";
|
$filter .= "(modifytimestamp>=".gmdate('YmdHis', $param['modified']).".0Z)";
|
||||||
}
|
}
|
||||||
$filter .= ')';
|
$filter .= ')';
|
||||||
$allValues = $this->vlvSortQuery($this->group_context, $filter, ['cn', 'gidNumber'], $order_by, $start, $offset, $group_total);
|
$allValues = $this->vlvSortQuery($this->group_context, $filter, ['cn', 'gidNumber', 'entryuuid'], $order_by, $start, $offset, $group_total);
|
||||||
$totalcount += $group_total;
|
$totalcount += $group_total;
|
||||||
foreach($allValues ?: [] as $allVals)
|
foreach($allValues ?: [] as $allVals)
|
||||||
{
|
{
|
||||||
@ -972,6 +975,7 @@ class Ldap
|
|||||||
{
|
{
|
||||||
$accounts[(string)-$allVals['gidnumber'][0]] = Array(
|
$accounts[(string)-$allVals['gidnumber'][0]] = Array(
|
||||||
'account_dn' => $allVals['dn'],
|
'account_dn' => $allVals['dn'],
|
||||||
|
'account_uuid' => $allVals['entryuuid'][0],
|
||||||
'account_id' => -$allVals['gidnumber'][0],
|
'account_id' => -$allVals['gidnumber'][0],
|
||||||
'account_lid' => Api\Translation::convert($allVals['cn'][0],'utf-8'),
|
'account_lid' => Api\Translation::convert($allVals['cn'][0],'utf-8'),
|
||||||
'account_type' => 'g',
|
'account_type' => 'g',
|
||||||
@ -1085,32 +1089,38 @@ class Ldap
|
|||||||
{
|
{
|
||||||
$name = Api\Ldap::quote(Api\Translation::convert($_name,Api\Translation::charset(),'utf-8'));
|
$name = Api\Ldap::quote(Api\Translation::convert($_name,Api\Translation::charset(),'utf-8'));
|
||||||
|
|
||||||
if (in_array($which, array('account_lid','account_email')) && $account_type !== 'u') // groups only support account_(lid|email)
|
$to_ldap = [
|
||||||
|
'account_lid' => 'cn',
|
||||||
|
'account_email' => static::MAIL_ATTR,
|
||||||
|
'account_uuid' => 'entryuuid',
|
||||||
|
'account_dn' => 'dn',
|
||||||
|
];
|
||||||
|
if (isset($to_ldap[$which]) && $account_type !== 'u') // groups only support account_(lid|email)
|
||||||
{
|
{
|
||||||
$attr = $which == 'account_lid' ? 'cn' : static::MAIL_ATTR;
|
$attr = $which == 'account_lid' ? 'cn' : static::MAIL_ATTR;
|
||||||
|
|
||||||
if (($sri = ldap_search($this->ds, $this->group_context, '(&('.$attr.'=' . $name . ")(objectclass=posixgroup)$this->group_filter)", array('gidNumber'))) &&
|
if (($data = ldap_search($this->group_context, '(&('.$attr.'=' . $name . ")(objectclass=posixgroup)$this->group_filter)", ['gidNumber'])) &&
|
||||||
($allValues = ldap_get_entries($this->ds, $sri)) &&
|
!empty($data['gidnumber'][0]))
|
||||||
!empty($allValues[0]['gidnumber'][0]))
|
|
||||||
{
|
{
|
||||||
return -$allValues[0]['gidnumber'][0];
|
return -$data['gidnumber'][0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$to_ldap = array(
|
$to_ldap = [
|
||||||
'account_lid' => 'uid',
|
'account_lid' => 'uid',
|
||||||
'account_email' => static::MAIL_ATTR,
|
'account_email' => static::MAIL_ATTR,
|
||||||
'account_fullname' => 'cn',
|
'account_fullname' => 'cn',
|
||||||
);
|
'account_uuid' => 'entryuuid',
|
||||||
|
'account_dn' => 'dn',
|
||||||
|
];
|
||||||
if (!isset($to_ldap[$which]) || $account_type === 'g')
|
if (!isset($to_ldap[$which]) || $account_type === 'g')
|
||||||
{
|
{
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber'))) &&
|
if (($data = $this->_ldap_search($this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber'))) &&
|
||||||
($allValues = ldap_get_entries($this->ds, $sri)) &&
|
!empty($data['uidNumber'][0]))
|
||||||
!empty($allValues[0]['uidnumber'][0]))
|
|
||||||
{
|
{
|
||||||
return (int)$allValues[0]['uidnumber'][0];
|
return (int)$data['uidnumber'][0];
|
||||||
}
|
}
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
@ -1181,15 +1191,14 @@ class Ldap
|
|||||||
|
|
||||||
$gid = abs($_gid); // our gid is negative!
|
$gid = abs($_gid); // our gid is negative!
|
||||||
|
|
||||||
$sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))",array('memberuid'));
|
$group = $this->_ldap_search($this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))", ['memberuid']);
|
||||||
$group = ldap_get_entries($this->ds, $sri);
|
|
||||||
|
|
||||||
$members = array();
|
$members = array();
|
||||||
if (isset($group[0]['memberuid']))
|
if ($group && isset($group['memberuid']))
|
||||||
{
|
{
|
||||||
unset($group[0]['memberuid']['count']);
|
unset($group['memberuid']['count']);
|
||||||
|
|
||||||
foreach($group[0]['memberuid'] as $lid)
|
foreach($group['memberuid'] as $lid)
|
||||||
{
|
{
|
||||||
if (($id = $this->name2id($lid, 'account_lid'))) // also return groups!
|
if (($id = $this->name2id($lid, 'account_lid'))) // also return groups!
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user