diff --git a/api/setup/setup.inc.php b/api/setup/setup.inc.php index 2a14868c9b..15065b27df 100644 --- a/api/setup/setup.inc.php +++ b/api/setup/setup.inc.php @@ -11,7 +11,7 @@ /* Basic information about this app */ $setup_info['api']['name'] = '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'; // maintenance release in sync with changelog in doc/rpm-build/debian.changes $setup_info['api']['versions']['maintenance_release'] = '23.1.20240624'; diff --git a/api/setup/tables_current.inc.php b/api/setup/tables_current.inc.php index f9404e6f06..dac20d1a09 100644 --- a/api/setup/tables_current.inc.php +++ b/api/setup/tables_current.inc.php @@ -64,7 +64,9 @@ $phpgw_baseline = array( 'account_expires' => array('type' => 'int','precision' => '4'), 'account_type' => array('type' => 'char','precision' => '1'), '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'), 'fk' => array(), @@ -553,4 +555,4 @@ $phpgw_baseline = array( 'ix' => array('account_id'), 'uc' => array() ) -); +); \ No newline at end of file diff --git a/api/setup/tables_update.inc.php b/api/setup/tables_update.inc.php index 7fb8ac21a3..1cb7dc1764 100644 --- a/api/setup/tables_update.inc.php +++ b/api/setup/tables_update.inc.php @@ -976,4 +976,20 @@ function api_upgrade23_1_006() )); 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'; } \ No newline at end of file diff --git a/api/src/Accounts/Ads.php b/api/src/Accounts/Ads.php index 667793ac94..d6d5a1b261 100644 --- a/api/src/Accounts/Ads.php +++ b/api/src/Accounts/Ads.php @@ -113,7 +113,7 @@ class Ads * @var array */ protected static $default_attributes = array( - 'objectsid', 'samaccounttype', 'samaccountname', + 'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid', ); /** @@ -122,7 +122,7 @@ class Ads * @var array */ protected static $user_attributes = array( - 'objectsid', 'samaccounttype', 'samaccountname', + 'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid', 'primarygroupid', 'givenname', 'sn', 'mail', 'displayname', 'telephonenumber', 'objectguid', 'useraccountcontrol', 'accountexpires', 'pwdlastset', 'whencreated', 'whenchanged', 'lastlogon', 'jpegphoto', @@ -134,7 +134,7 @@ class Ads * @var array */ protected static $group_attributes = array( - 'objectsid', 'samaccounttype', 'samaccountname', + 'objectsid', 'samaccounttype', 'samaccountname', 'entryuuid', 'objectguid', 'mail', 'whencreated', 'whenchanged', 'description', ); @@ -565,6 +565,7 @@ class Ads $group = array( 'account_dn' => $data['dn'], + 'account_uuid' => $data['entryuuid'][0], 'account_id' => $account_id, 'account_sid' => $sid, 'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]), @@ -644,6 +645,7 @@ class Ads $user = array( 'account_dn' => $data['dn'], + 'account_uuid' => $data['entryuuid'][0], 'account_id' => $account_id, 'account_sid' => $sid, 'account_guid' => $this->adldap->utilities()->decodeGuid($data['objectguid'][0]), @@ -1327,6 +1329,8 @@ class Ads 'account_fullname' => 'cn', 'account_sid' => 'objectsid', 'account_guid' => 'objectguid', + 'account_uuid' => 'entryuuid', + 'account_dn' => 'dn', ); $ret = false; if (isset($to_ldap[$which])) diff --git a/api/src/Accounts/Import.php b/api/src/Accounts/Import.php index 1eadec5ce7..b64c1415ff 100644 --- a/api/src/Accounts/Import.php +++ b/api/src/Accounts/Import.php @@ -325,7 +325,8 @@ class Import $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'); // 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; // 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'])); // query all groups in SQL - $sql_groups = $groups = $set_members = []; - foreach($GLOBALS['egw']->db->select(Sql::TABLE, 'account_id,account_lid', ['account_type' => 'g'], __LINE__, __FILE__) as $row) + $sql_groups = $uuid2account_id = $groups = $set_members = []; + 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']); + 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 $filter = ['type' => 'groups']; @@ -733,7 +738,13 @@ class Import } $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) { @@ -754,7 +765,7 @@ class Import } 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( 'location' => 'addgroup' ),False,True); // called for every app now, not only enabled ones) diff --git a/api/src/Accounts/Ldap.php b/api/src/Accounts/Ldap.php index ae092ad06e..c393f211b0 100644 --- a/api/src/Accounts/Ldap.php +++ b/api/src/Accounts/Ldap.php @@ -535,7 +535,7 @@ class Ldap } } 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 } @@ -543,6 +543,7 @@ class Ldap $group += array( 'account_dn' => $data['dn'], + 'account_uuid' => $data['entryuuid'][0], 'account_id' => -$data['gidnumber'][0], 'account_lid' => $data['cn'][0], '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)", 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 } @@ -593,6 +594,7 @@ class Ldap $utc_diff = date('Z'); $user = array( 'account_dn' => $data['dn'], + 'account_uuid' => $data['entryuuid'][0], 'account_id' => (int)$data['uidnumber'][0], 'account_lid' => $data['uid'][0], 'account_type' => 'u', @@ -893,7 +895,7 @@ class Ldap { $allValues = $this->vlvSortQuery($this->user_context, $filter, ['uid', 'uidNumber', 'givenname', 'sn', static::MAIL_ATTR, 'shadowExpire', 'createtimestamp', - 'modifytimestamp', 'objectclass', 'gidNumber', 'jpegphoto'], + 'modifytimestamp', 'objectclass', 'gidNumber', 'jpegphoto', 'entryuuid'], $order_by, $start, $offset, $totalcount); $utc_diff = date('Z'); @@ -904,6 +906,7 @@ class Ldap { $account = array( 'account_dn' => $allVals['dn'], + 'account_uuid' => $allVals['entryuuid'][0], 'account_id' => $allVals['uidnumber'][0], 'account_lid' => Api\Translation::convert($allVals['uid'][0], 'utf-8'), 'account_type' => 'u', @@ -963,7 +966,7 @@ class Ldap $filter .= "(modifytimestamp>=".gmdate('YmdHis', $param['modified']).".0Z)"; } $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; foreach($allValues ?: [] as $allVals) { @@ -972,6 +975,7 @@ class Ldap { $accounts[(string)-$allVals['gidnumber'][0]] = Array( 'account_dn' => $allVals['dn'], + 'account_uuid' => $allVals['entryuuid'][0], 'account_id' => -$allVals['gidnumber'][0], 'account_lid' => Api\Translation::convert($allVals['cn'][0],'utf-8'), 'account_type' => 'g', @@ -1085,32 +1089,38 @@ class Ldap { $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; - if (($sri = ldap_search($this->ds, $this->group_context, '(&('.$attr.'=' . $name . ")(objectclass=posixgroup)$this->group_filter)", array('gidNumber'))) && - ($allValues = ldap_get_entries($this->ds, $sri)) && - !empty($allValues[0]['gidnumber'][0])) + if (($data = ldap_search($this->group_context, '(&('.$attr.'=' . $name . ")(objectclass=posixgroup)$this->group_filter)", ['gidNumber'])) && + !empty($data['gidnumber'][0])) { - return -$allValues[0]['gidnumber'][0]; + return -$data['gidnumber'][0]; } } - $to_ldap = array( + $to_ldap = [ 'account_lid' => 'uid', 'account_email' => static::MAIL_ATTR, 'account_fullname' => 'cn', - ); + 'account_uuid' => 'entryuuid', + 'account_dn' => 'dn', + ]; if (!isset($to_ldap[$which]) || $account_type === 'g') { return False; } - if (($sri = ldap_search($this->ds, $this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber'))) && - ($allValues = ldap_get_entries($this->ds, $sri)) && - !empty($allValues[0]['uidnumber'][0])) + if (($data = $this->_ldap_search($this->user_context, '(&('.$to_ldap[$which].'=' . $name . ')(objectclass=posixaccount))', array('uidNumber'))) && + !empty($data['uidNumber'][0])) { - return (int)$allValues[0]['uidnumber'][0]; + return (int)$data['uidnumber'][0]; } return False; } @@ -1181,15 +1191,14 @@ class Ldap $gid = abs($_gid); // our gid is negative! - $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))",array('memberuid')); - $group = ldap_get_entries($this->ds, $sri); + $group = $this->_ldap_search($this->group_context,"(&(objectClass=posixGroup)(gidnumber=$gid))", ['memberuid']); $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! {