* Setup: when migrating accounts from SQL to LDAP or back, also migrate addressbook data

This commit is contained in:
Ralf Becker 2012-11-19 08:23:09 +00:00
parent 26de26873b
commit ec2ce0f943
4 changed files with 109 additions and 59 deletions

View File

@ -203,18 +203,43 @@ class addressbook_ldap
var $all_attributes = array(); var $all_attributes = array();
/** /**
* constructor of the class * LDAP configuration
*
* @var array values for keys "ldap_contact_context", "ldap_host", "ldap_context"
*/ */
function __construct() private $ldap_config;
/**
* constructor of the class
*
* @param array $ldap_config=null default use from $GLOBALS['egw_info']['server']
* @param resource $ds=null ldap connection to use
*/
function __construct(array $ldap_config=null, $ds=null)
{ {
//$this->db_data_cols = $this->stock_contact_fields + $this->non_contact_fields; //$this->db_data_cols = $this->stock_contact_fields + $this->non_contact_fields;
$this->accountName = $GLOBALS['egw_info']['user']['account_lid']; $this->accountName = $GLOBALS['egw_info']['user']['account_lid'];
$this->personalContactsDN = 'ou=personal,ou=contacts,'. $GLOBALS['egw_info']['server']['ldap_contact_context']; if ($ldap_config)
$this->sharedContactsDN = 'ou=shared,ou=contacts,'. $GLOBALS['egw_info']['server']['ldap_contact_context']; {
$this->ldap_config = $ldap_config;
}
else
{
$this->ldap_config =& $GLOBALS['egw_info']['server'];
}
$this->personalContactsDN = 'ou=personal,ou=contacts,'. $this->ldap_config['ldap_contact_context'];
$this->sharedContactsDN = 'ou=shared,ou=contacts,'. $this->ldap_config['ldap_contact_context'];
$this->connect(); if ($ds)
$this->ldapServerInfo = $GLOBALS['egw']->ldap->getLDAPServerInfo($GLOBALS['egw_info']['server']['ldap_contact_host']); {
$this->ds = $ds;
}
else
{
$this->connect();
}
$this->ldapServerInfo = $GLOBALS['egw']->ldap->getLDAPServerInfo($this->ldap_config['ldap_contact_host']);
foreach($this->schema2egw as $schema => $attributes) foreach($this->schema2egw as $schema => $attributes)
{ {
@ -241,14 +266,14 @@ class addressbook_ldap
// if ldap is NOT the contact repository, we only do accounts and need to use the account-data // if ldap is NOT the contact repository, we only do accounts and need to use the account-data
if (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap) if (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap)
{ {
$GLOBALS['egw_info']['server']['ldap_contact_host'] = $GLOBALS['egw_info']['server']['ldap_host']; $this->ldap_config['ldap_contact_host'] = $this->ldap_config['ldap_host'];
$GLOBALS['egw_info']['server']['ldap_contact_context'] = $GLOBALS['egw_info']['server']['ldap_context']; $this->ldap_config['ldap_contact_context'] = $this->ldap_config['ldap_context'];
$this->ds = $GLOBALS['egw']->ldap->ldapConnect(); $this->ds = $GLOBALS['egw']->ldap->ldapConnect();
} }
else else
{ {
$this->ds = $GLOBALS['egw']->ldap->ldapConnect( $this->ds = $GLOBALS['egw']->ldap->ldapConnect(
$GLOBALS['egw_info']['server']['ldap_contact_host'], $this->ldap_config['ldap_contact_host'],
$GLOBALS['egw_info']['user']['account_dn'], $GLOBALS['egw_info']['user']['account_dn'],
$GLOBALS['egw_info']['user']['passwd'] $GLOBALS['egw_info']['user']['passwd']
); );
@ -298,7 +323,7 @@ class addressbook_ldap
(isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid'])); (isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid']));
$filter = "(|(entryUUID=$contact_id)(uid=$contact_id))"; $filter = "(|(entryUUID=$contact_id)(uid=$contact_id))";
} }
$rows = $this->_searchLDAP($GLOBALS['egw_info']['server']['ldap_contact_context'], $rows = $this->_searchLDAP($this->ldap_config['ldap_contact_context'],
$filter, $this->all_attributes, ADDRESSBOOK_ALL); $filter, $this->all_attributes, ADDRESSBOOK_ALL);
return $rows ? $rows[0] : false; return $rows ? $rows[0] : false;
@ -339,7 +364,7 @@ class addressbook_ldap
$data['account_id'] == $GLOBALS['egw_info']['user']['account_id'])) $data['account_id'] == $GLOBALS['egw_info']['user']['account_id']))
{ {
// account // account
$baseDN = $GLOBALS['egw_info']['server']['ldap_context']; $baseDN = $this->ldap_config['ldap_context'];
$cn = false; $cn = false;
// we need an admin connection // we need an admin connection
$this->ds = $GLOBALS['egw']->ldap->ldapConnect(); $this->ds = $GLOBALS['egw']->ldap->ldapConnect();
@ -366,7 +391,7 @@ class addressbook_ldap
$attributes = array('dn','cn','objectClass','uid','mail'); $attributes = array('dn','cn','objectClass','uid','mail');
$contactUID = $this->data[$this->contacts_id]; $contactUID = $this->data[$this->contacts_id];
if(!empty($contactUID) && if(!empty($contactUID) &&
($result = ldap_search($this->ds, $GLOBALS['egw_info']['server']['ldap_contact_context'], ($result = ldap_search($this->ds, $this->ldap_config['ldap_contact_context'],
'(|(entryUUID='.ldap::quote($contactUID).')(uid='.ldap::quote($contactUID).'))', $attributes)) && '(|(entryUUID='.ldap::quote($contactUID).')(uid='.ldap::quote($contactUID).'))', $attributes)) &&
($oldContactInfo = ldap_get_entries($this->ds, $result)) && $oldContactInfo['count']) ($oldContactInfo = ldap_get_entries($this->ds, $result)) && $oldContactInfo['count'])
{ {
@ -547,7 +572,7 @@ class addressbook_ldap
foreach($keys as $entry) foreach($keys as $entry)
{ {
$entry = ldap::quote(is_array($entry) ? $entry['id'] : $entry); $entry = ldap::quote(is_array($entry) ? $entry['id'] : $entry);
if($result = ldap_search($this->ds, $GLOBALS['egw_info']['server']['ldap_contact_context'], if($result = ldap_search($this->ds, $this->ldap_config['ldap_contact_context'],
"(|(entryUUID=$entry)(uid=$entry))", $attributes)) "(|(entryUUID=$entry)(uid=$entry))", $attributes))
{ {
$contactInfo = ldap_get_entries($this->ds, $result); $contactInfo = ldap_get_entries($this->ds, $result);
@ -625,12 +650,12 @@ class addressbook_ldap
} }
elseif (!isset($filter['owner'])) elseif (!isset($filter['owner']))
{ {
$searchDN = $GLOBALS['egw_info']['server']['ldap_contact_context']; $searchDN = $this->ldap_config['ldap_contact_context'];
$addressbookType = ADDRESSBOOK_ALL; $addressbookType = ADDRESSBOOK_ALL;
} }
else else
{ {
$searchDN = $GLOBALS['egw_info']['server']['ldap_context']; $searchDN = $this->ldap_config['ldap_context'];
$addressbookType = ADDRESSBOOK_ACCOUNTS; $addressbookType = ADDRESSBOOK_ACCOUNTS;
} }
@ -955,7 +980,7 @@ class addressbook_ldap
/** /**
* check if $baseDN exists. If not create it * check if $baseDN exists. If not create it
* *
* @param string $baseDN cn=xxx,ou=yyy,ou=contacts,$GLOBALS['egw_info']['server']['ldap_contact_context'] * @param string $baseDN cn=xxx,ou=yyy,ou=contacts,$this->ldap_config['ldap_contact_context']
* @return boolean/string false on success or string with error-message * @return boolean/string false on success or string with error-message
*/ */
function _check_create_dn($baseDN) function _check_create_dn($baseDN)
@ -975,8 +1000,8 @@ class addressbook_ldap
list(,$ou) = explode(',',$baseDN); list(,$ou) = explode(',',$baseDN);
foreach(array( foreach(array(
'ou=contacts,'.$GLOBALS['egw_info']['server']['ldap_contact_context'], 'ou=contacts,'.$this->ldap_config['ldap_contact_context'],
$ou.',ou=contacts,'.$GLOBALS['egw_info']['server']['ldap_contact_context'], $ou.',ou=contacts,'.$this->ldap_config['ldap_contact_context'],
$baseDN, $baseDN,
) as $dn) ) as $dn)
{ {

View File

@ -868,58 +868,72 @@ class addressbook_so
} }
/** /**
* Migrates an SQL contact storage to LDAP or SQL-LDAP * Migrates an SQL contact storage to LDAP, SQL-LDAP or back to SQL
* *
* @param string $type "contacts" (default), "contacts+accounts" or "contacts+accounts-back" (sql-ldap!) * @param string|array $type comma-separated list or array of:
* - "contacts" contacts to ldap
* - "accounts" accounts to ldap
* - "accounts-back" accounts back to sql (for sql-ldap!)
* - "sql" contacts and accounts to sql
*/ */
function migrate2ldap($type) function migrate2ldap($type)
{ {
//error_log(__METHOD__."(".array2string($type).")");
$sql_contacts = new addressbook_sql(); $sql_contacts = new addressbook_sql();
$ldap_contacts = new addressbook_ldap(); // we need an admin connection
$ds = $GLOBALS['egw']->ldap->ldapConnect();
$ldap_contacts = new addressbook_ldap(null, $ds);
if (!is_array($type)) $type = explode(',', $type);
$start = $n = 0; $start = $n = 0;
$num = 100; $num = 100;
while ($type != 'sql' && ($contacts = $sql_contacts->search(false,false,'n_family,n_given','','',false,'AND',
array($start,$num),$type != 'contacts,accounts' ? array('contact_owner != 0') : false)))
{
// very worse hack, until Ralf finds a better solution
// when migrating data, we need to bind as global ldap admin account
// and not as currently logged in user
$ldap_contacts->ds = $GLOBALS['egw']->ldap->ldapConnect();
foreach($contacts as $contact)
{
if ($contact['account_id']) $contact['id'] = $GLOBALS['egw']->accounts->id2name($contact['account_id']);
$ldap_contacts->data = $contact; // direction SQL --> LDAP, either only accounts, or only contacts or both
$n++; if (($do = array_intersect($type, array('contacts', 'accounts'))))
if (!($err = $ldap_contacts->save()))
{
echo '<p style="margin: 0px;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> LDAP</p>\n";
}
else
{
echo '<p style="margin: 0px; color: red;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."</p>\n";
}
}
$start += $num;
}
if ($type == 'contacts,accounts-back' || $type == 'sql') // migrate the accounts to sql
{ {
// very worse hack, until Ralf finds a better solution $filter = count($do) == 2 ? null :
// when migrating data, we need to bind as global ldap admin account array($do[0] == 'contacts' ? 'contact_owner != 0' : 'contact_owner = 0');
// and not as currently logged in user
$ldap_contacts->ds = $GLOBALS['egw']->ldap->ldapConnect(); while (($contacts = $sql_contacts->search(false,false,'n_family,n_given','','',false,'AND',
foreach($ldap_contacts->search(false,false,'n_family,n_given','','',false,'AND', array($start,$num),$filter)))
false,$type == 'sql'?null:array('owner' => 0)) as $contact)
{ {
foreach($contacts as $contact)
{
if ($contact['account_id']) $contact['id'] = $GLOBALS['egw']->accounts->id2name($contact['account_id']);
$ldap_contacts->data = $contact;
$n++;
if (!($err = $ldap_contacts->save()))
{
echo '<p style="margin: 0px;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '')." --> LDAP</p>\n";
}
else
{
echo '<p style="margin: 0px; color: red;">'.$n.': '.$contact['n_fn'].
($contact['org_name'] ? ' ('.$contact['org_name'].')' : '').': '.$err."</p>\n";
}
}
$start += $num;
}
}
// direction LDAP --> SQL: either "sql" (contacts and accounts) or "accounts-back" (only accounts)
if (($do = array_intersect(array('accounts-back','sql'), $type)))
{
//error_log(__METHOD__."(".array2string($type).") do=".array2string($type));
$filter = in_array('sql', $do) ? null : array('owner' => 0);
foreach($ldap_contacts->search(false,false,'n_family,n_given','','',false,'AND',
false, $filter) as $contact)
{
//error_log(__METHOD__."(".array2string($type).") do=".array2string($type)." migrating ".array2string($contact));
if ($contact['jpegphoto']) // photo is NOT read by LDAP backend on search, need to do an extra read if ($contact['jpegphoto']) // photo is NOT read by LDAP backend on search, need to do an extra read
{ {
$contact = $ldap_contacts->read($contact['id']); $contact = $ldap_contacts->read($contact['id']);
} }
unset($contact['id']); // ldap uid/account_lid unset($contact['id']); // ldap uid/account_lid
if ($type != 'sql' && $contact['account_id'] && ($old = $sql_contacts->read(array('account_id' => $contact['account_id'])))) if ($contact['account_id'] && ($old = $sql_contacts->read(array('account_id' => $contact['account_id']))))
{ {
$contact['id'] = $old['id']; $contact['id'] = $old['id'];
} }

View File

@ -990,10 +990,9 @@ class setup
if (!$config) if (!$config)
{ {
// load the configuration from the database // load the configuration from the database
$this->db->select($this->config_table,'config_name,config_value', foreach($this->db->select($this->config_table,'config_name,config_value',
"config_name LIKE 'ldap%' OR config_name LIKE 'account_%' OR config_name LIKE '%encryption%' OR config_name='auth_type'",__LINE__,__FILE__); "config_name LIKE 'ldap%' OR config_name LIKE 'account_%' OR config_name LIKE '%encryption%' OR config_name='auth_type'",
__LINE__,__FILE__) as $row)
while(($row = $this->db->row(true)))
{ {
$GLOBALS['egw_info']['server'][$row['config_name']] = $config[$row['config_name']] = $row['config_value']; $GLOBALS['egw_info']['server'][$row['config_name']] = $config[$row['config_name']] = $row['config_value'];
} }

View File

@ -257,6 +257,18 @@ class setup_cmd_ldap extends setup_cmd
$accounts_obj->set_members($account['members'],$account_id); $accounts_obj->set_members($account['members'],$account_id);
} }
} }
// migrate addressbook data
$GLOBALS['egw_info']['user']['apps']['admin'] = true; // otherwise migration will not run in setup!
$addressbook = new addressbook_so();
foreach($this->as_array() as $name => $value)
{
if (substr($name, 5) == 'ldap_')
{
$GLOBALS['egw_info']['server'][$name] = $value;
}
}
$addressbook->migrate2ldap($to_ldap ? 'accounts' : 'accounts-back');
$this->restore_db(); $this->restore_db();
return lang('%1 users and %2 groups created, %3 errors',$accounts_created,$groups_created,$errors). return lang('%1 users and %2 groups created, %3 errors',$accounts_created,$groups_created,$errors).
@ -397,7 +409,7 @@ class setup_cmd_ldap extends setup_cmd
$this->test_ldap = new ldap(); $this->test_ldap = new ldap();
$error_rep = error_reporting(); $error_rep = error_reporting();
//error_reporting($error_rep & ~E_WARNING); // switch warnings of, in case they are on error_reporting($error_rep & ~E_WARNING); // switch warnings of, in case they are on
ob_start(); ob_start();
$ds = $this->test_ldap->ldapConnect($this->ldap_host,$dn,$pw); $ds = $this->test_ldap->ldapConnect($this->ldap_host,$dn,$pw);
ob_end_clean(); ob_end_clean();