From 7be62b431fd393bfe51bc9ea4686695819af4b3f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 26 Aug 2010 20:22:02 +0000 Subject: [PATCH] moved logic of account-migration to setup_cmd_ldap and using setup_cmd_ldap for account_migration.php, that way we can also create the ldap-structur during the migration --- setup/account_migration.php | 140 +++------------- setup/inc/class.setup_cmd_ldap.inc.php | 213 ++++++++++++++++++++++++- 2 files changed, 230 insertions(+), 123 deletions(-) diff --git a/setup/account_migration.php b/setup/account_migration.php index 22ab931f72..77d4e05c95 100644 --- a/setup/account_migration.php +++ b/setup/account_migration.php @@ -21,15 +21,6 @@ if (!$GLOBALS['egw_setup']->auth('Config') || $_POST['cancel']) } // Does not return unless user is authorized -// the migration script needs a session to store the accounts -session_name('setup_session'); -session_set_cookie_params(0,'/',$GLOBALS['egw_setup']->cookie_domain); -if (isset($_REQUEST['setup_session'])) -{ - session_id($_REQUEST['setup_session']); -} -session_start(); - $tpl_root = $GLOBALS['egw_setup']->html->setup_tpl_dir('setup'); $setup_tpl = CreateObject('phpgwapi.Template',$tpl_root); $setup_tpl->set_file(array( @@ -69,7 +60,7 @@ if (!is_object($GLOBALS['egw_setup']->db)) { $GLOBALS['egw_setup']->loaddb(); } -// Load configuration values account_repository and auth_type, a setup has not yet done so +// Load configuration values account_repository and auth_type, as setup has not yet done so foreach($GLOBALS['egw_setup']->db->select($GLOBALS['egw_setup']->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__) as $row) @@ -87,40 +78,23 @@ $direction = strtoupper($from).' --> '.strtoupper($to); $GLOBALS['egw_setup']->html->show_header($direction,False,'config',$GLOBALS['egw_setup']->ConfigDomain . '(' . $GLOBALS['egw_domain'][$GLOBALS['egw_setup']->ConfigDomain]['db_type'] . ')'); +// create base one level off ldap_context +$base_parts = explode(',',$GLOBALS['egw_info']['server']['ldap_context']); +array_shift($base_parts); + +$cmd = new setup_cmd_ldap(array( + 'domain' => $GLOBALS['egw_setup']->ConfigDomain, + 'sub_command' => 'migrate_to_'.$to, + // in regular setup we only support one ldap root user, setting him as admin user too + 'ldap_admin' => $GLOBALS['egw_info']['server']['ldap_root_dn'], + 'ldap_admin_pw' => $GLOBALS['egw_info']['server']['ldap_root_pw'], + 'ldap_base' => implode(',',$base_parts), +)+$GLOBALS['egw_info']['server']); + if (!$_POST['migrate']) { - // fetch and display the accounts of the NOT set $from repository - $GLOBALS['egw_info']['server']['account_repository'] = $from; - $GLOBALS['egw_setup']->setup_account_object($GLOBALS['egw_info']['server']); - - // fetch all users and groups - $accounts = $GLOBALS['egw_setup']->accounts->search(array( - 'type' => 'both', - )); - // fetch the complete data (search reads not everything), plus the members(hips) - foreach($accounts as $account_id => $account) - { - if ($account_id != $account['account_id']) // not all backends have as key the account_id - { - unset($accounts[$account_id]); - $account_id = $account['account_id']; - } - $accounts[$account_id] = $GLOBALS['egw_setup']->accounts->read($account_id); - - if ($account['account_type'] == 'g') - { - $accounts[$account_id]['members'] = $GLOBALS['egw_setup']->accounts->members($account_id,true); - } - else - { - $accounts[$account_id]['memberships'] = $GLOBALS['egw_setup']->accounts->memberships($account_id,true); - } - } - //_debug_array($accounts); - // store the complete info in the session to be availible after user selected what to migrate - // we cant instanciate to account-repositories at the same time, as the backend-classes have identical names - $_SESSION['all_accounts'] =& $accounts; - + $accounts = $cmd->accounts($from == 'ldap'); + // now outputting the account selection $setup_tpl->set_block('migration','header','header'); $setup_tpl->set_block('migration','user_list','user_list'); @@ -166,85 +140,9 @@ if (!$_POST['migrate']) } else // do the migration { - $GLOBALS['egw_info']['server']['account_repository'] = $to; - $GLOBALS['egw_setup']->setup_account_object($GLOBALS['egw_info']['server']); - - $target = strtoupper($to); - $accounts =& $_SESSION['all_accounts']; - - if($_POST['users']) - { - foreach($_POST['users'] as $account_id) - { - if (!isset($accounts[$account_id])) continue; - - // check if user already exists - if ($GLOBALS['egw_setup']->accounts->exists($account_id)) - { - echo '

'.lang('%1 already exists in %2.',lang('User')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - continue; - } - if ($to == 'ldap') - { - if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) - { - $accounts[$account_id]['homedirectory'] = $GLOBALS['egw_info']['server']['ldap_account_home'] . '/' . $accounts[$account_id]['account_lid']; - $accounts[$account_id]['loginshell'] = $GLOBALS['egw_info']['server']['ldap_account_shell']; - } - $accounts[$account_id]['account_passwd'] = hash_sql2ldap($accounts[$account_id]['account_pwd']); - } - else - { - if ($accounts[$account_id]['account_pwd'][0] != '{') // plain has to be explicitly specified for sql, in ldap it's the default - { - $accounts[$account_id]['account_passwd'] = '{PLAIN}'.$accounts[$account_id]['account_pwd']; - } - else - { - $accounts[$account_id]['account_passwd'] = $accounts[$account_id]['account_pwd']; - } - } - unset($accounts[$account_id]['person_id']); - - if (!$GLOBALS['egw_setup']->accounts->save($accounts[$account_id])) - { - echo '

'.lang('Creation of %1 in %2 failed !!!',lang('User')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - continue; - } - $GLOBALS['egw_setup']->accounts->set_memberships($accounts[$account_id]['memberships'],$account_id); - echo '

'.lang('%1 created in %2.',lang('User')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - } - } - if($_POST['groups']) - { - foreach($_POST['groups'] as $account_id) - { - if (!isset($accounts[$account_id])) continue; - - // check if group already exists - if (!$GLOBALS['egw_setup']->accounts->exists($account_id)) - { - if (!$GLOBALS['egw_setup']->accounts->save($accounts[$account_id])) - { - echo '

'.lang('Creation of %1 in %2 failed !!!',lang('Group')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - continue; - } - echo '

'.lang('%1 created in %2.',lang('Group')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - } - else - { - echo '

'.lang('%1 already exists in %2.',lang('Group')." $account_id ({$accounts[$account_id]['account_lid']})",$target)."

\n"; - - if ($GLOBALS['egw_setup']->accounts->id2name($account_id) != $accounts[$account_id]['account_lid']) - { - continue; // different group under that gidnumber! - } - } - // now saving / updating the memberships - $GLOBALS['egw_setup']->accounts->set_members($accounts[$account_id]['members'],$account_id); - } - } - echo '

'.lang('Export has been completed!')."

\n"; + $cmd->only = array_merge((array)$_POST['users'],(array)$_POST['groups']); + $cmd->verbose = true; + echo '

'.str_replace("\n","

\n

",$cmd->run())."

\n"; echo '

'.lang('Click here to return to setup.')."

\n"; } diff --git a/setup/inc/class.setup_cmd_ldap.inc.php b/setup/inc/class.setup_cmd_ldap.inc.php index 9dda24fde9..ed2b50961b 100644 --- a/setup/inc/class.setup_cmd_ldap.inc.php +++ b/setup/inc/class.setup_cmd_ldap.inc.php @@ -45,7 +45,7 @@ class setup_cmd_ldap extends setup_cmd */ function __construct($domain,$ldap_host=null,$ldap_suffix=null,$ldap_admin=null,$ldap_admin_pw=null, $ldap_base=null,$ldap_root_dn=null,$ldap_root_pw=null,$ldap_context=null,$ldap_search_filter=null, - $ldap_group_context=null,$sub_command='create_ldap') + $ldap_group_context=null,$sub_command='create_ldap',$ldap_encryption_type='des') { if (!is_array($domain)) { @@ -61,7 +61,8 @@ class setup_cmd_ldap extends setup_cmd 'ldap_context' => $ldap_context, 'ldap_search_filter' => $ldap_search_filter, 'ldap_group_context' => $ldap_group_context, - 'sub_command' => $sub_command + 'sub_command' => $sub_command, + 'ldap_encryption_type' => $ldap_encryption_type, ); } //echo __CLASS__.'::__construct()'; _debug_array($domain); @@ -101,6 +102,10 @@ class setup_cmd_ldap extends setup_cmd case 'users_ldap': $msg = $this->users(); break; + case 'migrate_to_ldap': + case 'migrate_to_sql': + $msg = $this->migrate($this->sub_command == 'migrate_to_ldap'); + break; case 'create_ldap': default: $msg = $this->create(); @@ -108,6 +113,210 @@ class setup_cmd_ldap extends setup_cmd } return $msg; } + + /** + * Migrate to other account storage + * + * @param boolean $to_ldap true: sql --> ldap, false: ldap --> sql + * @return string with success message + * @throws Exception on error + */ + private function migrate($to_ldap) + { + $msg = array(); + // if migrating to ldap, check ldap and create context if not yet exiting + if ($to_ldap) + { + $msg[] = $this->create(); + } + else + { + $msg[] = $this->connect(); + } + // read accounts from old store + $accounts = $this->accounts(!$to_ldap); + + // instanciate accounts obj for new store + $accounts_obj = $this->accounts_obj($to_ldap); + + $accounts_created = $groups_created = $errors = 0; + $target = $to_ldap ? 'LDAP' : 'SQL'; + foreach($accounts as $account_id => $account) + { + if (isset($this->only) && !in_array($account_id,$this->only)) + { + continue; + } + $what = ($account['account_type'] == 'u' ? lang('User') : lang('Group')).' '. + $account_id.' ('.$account['account_lid'].')'; + + if ($account['account_type'] == 'u') + { + if ($accounts_obj->exists($account_id)) + { + $msg[] = lang('%1 already exists in %2.',$what,$target); + $errors++; + continue; + } + if ($to_ldap) + { + if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) + { + $account['homedirectory'] = $GLOBALS['egw_info']['server']['ldap_account_home'] . '/' . $account['account_lid']; + $account['loginshell'] = $GLOBALS['egw_info']['server']['ldap_account_shell']; + } + $account['account_passwd'] = self::hash_sql2ldap($account['account_pwd']); + } + else + { + if ($account['account_pwd'][0] != '{') // plain has to be explicitly specified for sql, in ldap it's the default + { + $account['account_passwd'] = '{PLAIN}'.$account['account_pwd']; + } + else + { + $account['account_passwd'] = $account['account_pwd']; + } + } + unset($account['person_id']); + + if (!$accounts_obj->save($account)) + { + $msg[] = lang('Creation of %1 in %2 failed !!!',$what,$target); + $errors++; + continue; + } + $accounts_obj->set_memberships($account['memberships'],$account_id); + $msg[] = lang('%1 created in %2.',$what,$target); + $accounts_created++; + } + else + { + // check if group already exists + if (!$accounts_obj->exists($account_id)) + { + if (!$accounts_obj->save($account)) + { + $msg[] = lang('Creation of %1 in %2 failed !!!',$what,$target); + ++$errors; + continue; + } + $msg[] = lang('%1 created in %2.',$what,$target); + $groups_created++; + } + else + { + $msg[] = lang('%1 already exists in %2.',$what,$target); + + if ($accounts_obj->id2name($account_id) != $account['account_lid']) + { + ++$errors; + continue; // different group under that gidnumber! + } + } + // now saving / updating the memberships + $accounts_obj->set_members($account['members'],$account_id); + } + } + return lang('%1 users and %2 groups created, %3 errors',$accounts_created,$groups_created,$errors). + ($errors || $this->verbose ? "\n- ".implode("\n- ",$msg) : ''); + } + + /** + * Convert SQL hash to LDAP hash + * + * @param string $hash + * @return string + */ + public static function hash_sql2ldap($hash) + { + $type = $GLOBALS['egw_info']['server']['sql_encryption_type']; + + if (preg_match('/^\\{(.*)\\}(.*)$/',$hash,$matches)) + { + $type = $matches[1]; + $hash = $matches[2]; + } + switch(strtolower($type)) + { + case '': // not set sql_encryption_type + case 'md5': + $hash = '{md5}' . base64_encode(pack("H*",$hash)); + break; + case 'crypt': + $hash = '{crypt}' . $hash; + break; + + case 'plain': + break; + } + return $hash; + } + + /** + * Read all accounts from sql or ldap + * + * @param boolean $from_ldap=true true: ldap, false: sql + * @return array + */ + public function accounts($from_ldap=true) + { + $accounts_obj = $this->accounts_obj($from_ldap); + //error_log(__METHOD__."(from_ldap=".array2string($from_ldap).') get_class(accounts_obj->backend)='.get_class($accounts_obj->backend)); + + $accounts = $accounts_obj->search(array('type' => 'both')); + + foreach($accounts as $account_id => &$account) + { + if ($account_id != $account['account_id']) // not all backends have as key the account_id + { + unset($account); + $account_id = $account['account_id']; + } + $account = $accounts_obj->read($account_id); + + if ($account['account_type'] == 'g') + { + $account['members'] = $accounts_obj->members($account_id,true); + } + else + { + $account['memberships'] = $accounts_obj->memberships($account_id,true); + } + } + return $accounts; + } + + /** + * Instancate accounts object from either sql of ldap + * + * @param boolean $ldap true: ldap, false: sql + * @return accounts + */ + private function accounts_obj($ldap) + { + static $enviroment_setup; + if (!$enviroment_setup) + { + parent::_setup_enviroment($this->domain); + $enviroment_setup = true; + } + if ($ldap) $this->connect(); // throws exception, if it can NOT connect + + // otherwise search does NOT work, as accounts_sql uses addressbook_bo for it + $GLOBALS['egw_info']['server']['account_repository'] = $ldap ? 'ldap' : 'sql'; + + if (!self::$egw_setup->setup_account_object( + array( + 'account_repository' => $GLOBALS['egw_info']['server']['account_repository'], + ) + $this->as_array()) || + !is_a(self::$egw_setup->accounts,'accounts') || + !is_a(self::$egw_setup->accounts->backend,'accounts_'.($ldap?'ldap':'sql'))) + { + throw new Exception(lang("Can NOT instancate accounts object for %1",$ldap?'LDAP':'SQL')); + } + return self::$egw_setup->accounts; + } /** * Connect to ldap server