Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like setup_cmd_ldap often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use setup_cmd_ldap, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 55 | class setup_cmd_ldap extends setup_cmd |
||
| 56 | { |
||
| 57 | /** |
||
| 58 | * Allow to run this command via setup-cli |
||
| 59 | */ |
||
| 60 | const SETUP_CLI_CALLABLE = true; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * Instance of ldap object |
||
| 64 | * |
||
| 65 | * @var ldap |
||
| 66 | */ |
||
| 67 | private $test_ldap; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * Constructor |
||
| 71 | * |
||
| 72 | * @param string/array $domain domain-name to customize the defaults or array with all parameters |
||
| 73 | * @param string $ldap_host =null |
||
| 74 | * @param string $ldap_suffix =null base of the whole ldap install, default "dc=local" |
||
| 75 | * @param string $ldap_admin =null root-dn needed to create new entries in the suffix |
||
| 76 | * @param string $ldap_admin_pw =null |
||
| 77 | * @param string $ldap_base =null base of the instance, default "o=$domain,$suffix" |
||
| 78 | * @param string $ldap_root_dn =null root-dn used for the instance, default "cn=admin,$base" |
||
| 79 | * @param string $ldap_root_pw =null |
||
| 80 | * @param string $ldap_context =null ou for accounts, default "ou=accounts,$base" |
||
| 81 | * @param string $ldap_search_filter =null search-filter for accounts, default "(uid=%user)" |
||
| 82 | * @param string $ldap_group_context =null ou for groups, default "ou=groups,$base" |
||
| 83 | * @param string $sub_command ='create_ldap' 'create_ldap', 'test_ldap', 'test_ldap_root', see exec method |
||
| 84 | * @param string $ldap_encryption_type ='des' |
||
| 85 | * @param boolean $truncate_egw_accounts =false truncate accounts table before migration to SQL |
||
| 86 | */ |
||
| 87 | function __construct($domain,$ldap_host=null,$ldap_suffix=null,$ldap_admin=null,$ldap_admin_pw=null, |
||
| 113 | |||
| 114 | /** |
||
| 115 | * run the command: test or create the ldap connection and hierarchy |
||
| 116 | * |
||
| 117 | * @param boolean $check_only =false only run the checks (and throw the exceptions), but not the command itself |
||
| 118 | * @return string success message |
||
| 119 | * @throws Exception(lang('Wrong credentials to access the header.inc.php file!'),2); |
||
| 120 | * @throws Exception('header.inc.php not found!'); |
||
| 121 | */ |
||
| 122 | protected function exec($check_only=false) |
||
| 171 | |||
| 172 | const sambaSID = 'sambasid'; |
||
|
|
|||
| 173 | |||
| 174 | /** |
||
| 175 | * Change uidNumber and gidNumber to match rid (last part of sambaSID) |
||
| 176 | * |
||
| 177 | * First run it with --dry-run to get ids to change / admin-cli command to change ids in EGroupware. |
||
| 178 | * Then run admin/admin-cli.php --change-account-id and after this command again without --dry-run. |
||
| 179 | * After that you need to run the given chown.php command to change filesystem uid/gid in samba share. |
||
| 180 | * |
||
| 181 | * @param boolean $check_only =false true: only connect and output necessary commands |
||
| 182 | */ |
||
| 183 | private function sid2uidnumber($check_only=false) |
||
| 262 | |||
| 263 | /** |
||
| 264 | * Copy given attributes of accounts of one ldap to active directory |
||
| 265 | * |
||
| 266 | * @param boolean $check_only =false true: only connect and output necessary commands |
||
| 267 | */ |
||
| 268 | private function copy2ad($check_only=false) |
||
| 269 | { |
||
| 270 | $msg = array(); |
||
| 271 | $attrs = $rename = array(); |
||
| 272 | foreach(explode(',', $this->attributes) as $attr) |
||
| 273 | { |
||
| 274 | if ($attr[0] == '@' || // copy whole objectclass without renaming, eg. @inetOrgPerson |
||
| 275 | strpos($attr, '=') === false) |
||
| 276 | { |
||
| 277 | $attrs[] = $attr; |
||
| 278 | } |
||
| 279 | else |
||
| 280 | { |
||
| 281 | list($to, $from) = explode('=', $attr); |
||
| 282 | if ($from) $attrs[] = $from; |
||
| 283 | $rename[strtolower($from)] = $to; |
||
| 284 | } |
||
| 285 | } |
||
| 286 | $ignore_attr = array_flip(array('dn', 'objectclass', 'cn', 'userpassword')); |
||
| 287 | if (!in_array('uid', $attrs)) |
||
| 288 | { |
||
| 289 | $attrs[] = 'uid'; // need to match account |
||
| 290 | $ignore_attr['uid'] = true; |
||
| 291 | } |
||
| 292 | // connect to destination ads |
||
| 293 | if (empty($this->ads_context)) |
||
| 294 | { |
||
| 295 | $this->ads_context = 'CN=Users,DC='.implode(',DC=', explode('.', $this->ads_domain)); |
||
| 296 | } |
||
| 297 | if (empty($this->ads_admin_user)) $this->ads_admin_user = 'Administrator'; |
||
| 298 | $admin_dn = strpos($this->ads_admin_user, '=') !== false ? $this->ads_admin_user : |
||
| 299 | 'CN='.$this->ads_admin_user.','.$this->ads_context; |
||
| 300 | switch($this->ads_connection) |
||
| 301 | { |
||
| 302 | case 'ssl': |
||
| 303 | $url = 'ldaps://'.$this->ads_host.'/'; |
||
| 304 | break; |
||
| 305 | case 'tls': |
||
| 306 | $url = 'tls://'.$this->ads_host.'/'; |
||
| 307 | break; |
||
| 308 | default: |
||
| 309 | $url = 'ldap://'.$this->ads_host.'/'; |
||
| 310 | break; |
||
| 311 | } |
||
| 312 | $this->connect($admin_dn, $this->ads_admin_pw, $url); |
||
| 313 | $ads = $this->test_ldap; unset($this->test_ldap); |
||
| 314 | |||
| 315 | // check if ads base does exist |
||
| 316 | if (!@ldap_read($ads->ds, $this->ads_context, 'objectClass=*')) |
||
| 317 | { |
||
| 318 | throw new Api\Exception\WrongUserinput(lang('Ads dn "%1" NOT found!',$this->ads_context)); |
||
| 319 | } |
||
| 320 | |||
| 321 | // connect to source ldap |
||
| 322 | $this->connect(); |
||
| 323 | |||
| 324 | // check if ldap base does exist |
||
| 325 | View Code Duplication | if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*')) |
|
| 326 | { |
||
| 327 | throw new Api\Exception\WrongUserinput(lang('Base dn "%1" NOT found!',$this->ldap_base)); |
||
| 328 | } |
||
| 329 | |||
| 330 | if (!($sr = ldap_search($this->test_ldap->ds,$this->ldap_base, |
||
| 331 | $search = $this->no_sid_check ? '(objectClass=posixAccount)' : |
||
| 332 | '(&(objectClass=posixAccount)('.self::sambaSID.'=*)(!(uid=*$)))', $attrs)) || |
||
| 333 | !($entries = ldap_get_entries($this->test_ldap->ds, $sr))) |
||
| 334 | { |
||
| 335 | throw new Api\Exception(lang('Error searching "dn=%1" for "%2"!',$this->ldap_base, $search)); |
||
| 336 | } |
||
| 337 | $changed = 0; |
||
| 338 | $utc_diff = null; |
||
| 339 | foreach($entries as $key => $entry) |
||
| 340 | { |
||
| 341 | if ($key === 'count') continue; |
||
| 342 | |||
| 343 | $entry_arr = Api\Ldap::result2array($entry); |
||
| 344 | $uid = $entry_arr['uid']; |
||
| 345 | $entry = array_diff_key($entry_arr, $ignore_attr); |
||
| 346 | |||
| 347 | if (!($sr = ldap_search($ads->ds, $this->ads_context, |
||
| 348 | $search='(&(objectClass=user)(sAMAccountName='.Api\Ldap::quote($uid).'))', array('dn'))) || |
||
| 349 | !($dest = ldap_get_entries($ads->ds, $sr))) |
||
| 350 | { |
||
| 351 | $msg[] = lang('User "%1" not found!', $uid); |
||
| 352 | continue; |
||
| 353 | } |
||
| 354 | $dn = $dest[0]['dn']; |
||
| 355 | if (isset($rename[''])) $entry[''] = ''; |
||
| 356 | // special handling for copying shadowExpires to accountExpires |
||
| 357 | if (strtolower($rename['shadowexpire']) === 'accountexpires') |
||
| 358 | { |
||
| 359 | // need to write accountExpires for never expiring account, as samba-tool classicupgrade sets it to 2038-01-19 |
||
| 360 | if (!isset($entry['shadowexpire']) || !$entry['shadowexpire']) |
||
| 361 | { |
||
| 362 | $entry['shadowexpire'] = accounts_ads::EXPIRES_NEVER; |
||
| 363 | } |
||
| 364 | else |
||
| 365 | { |
||
| 366 | if (is_null($utc_diff)) $utc_diff = date('Z'); |
||
| 367 | $entry['shadowexpire'] = accounts_ads::convertUnixTimeToWindowsTime( |
||
| 368 | $entry['shadowexpire']*24*3600+$utc_diff); // ldap time to unixTime |
||
| 369 | } |
||
| 370 | } |
||
| 371 | $update = array(); |
||
| 372 | foreach($entry as $attr => $value) |
||
| 373 | { |
||
| 374 | if ($value || $attr === '') |
||
| 375 | { |
||
| 376 | $to = isset($rename[$attr]) ? $rename[$attr] : $attr; |
||
| 377 | $prefix = null; |
||
| 378 | if ($to[0] == '{') // eg. {smtp:}proxyAddresses=forwardTo |
||
| 379 | { |
||
| 380 | list($prefix, $to) = explode('}', substr($to, 1)); |
||
| 381 | } |
||
| 382 | foreach((array)$value as $val) |
||
| 383 | { |
||
| 384 | if (isset($update[$to])) |
||
| 385 | { |
||
| 386 | if (!is_array($update[$to])) $update[$to] = array($update[$to]); |
||
| 387 | // we need to check (caseinsensitive) if value already exists in set |
||
| 388 | // as AD chokes on doublicate values "Type or value exists" |
||
| 389 | foreach($update[$to] as $v) |
||
| 390 | { |
||
| 391 | if (!strcasecmp($v, $prefix.$val)) continue 2; |
||
| 392 | } |
||
| 393 | $update[$to][] = $prefix.$val; |
||
| 394 | } |
||
| 395 | else |
||
| 396 | { |
||
| 397 | $update[$to] = $prefix.$val; |
||
| 398 | } |
||
| 399 | } |
||
| 400 | } |
||
| 401 | } |
||
| 402 | if ($check_only) |
||
| 403 | { |
||
| 404 | print_r($dn); |
||
| 405 | print_r($update); |
||
| 406 | continue; |
||
| 407 | } |
||
| 408 | if ($update && !ldap_modify($ads->ds, $dn, $update)) |
||
| 409 | { |
||
| 410 | error_log(lang('Failed updating user "%1" dn="%2"!', $uid, $dn).' '.ldap_error($ads->ds)); |
||
| 411 | } |
||
| 412 | else |
||
| 413 | { |
||
| 414 | print_r(lang('User "%1" dn="%2" successful updated.', $uid, $dn)."\n"); |
||
| 415 | $changed++; |
||
| 416 | } |
||
| 417 | } |
||
| 418 | if ($check_only) return lang("%1 accounts to copy found.", count($entries)); |
||
| 419 | |||
| 420 | return "Copied data of $changed accounts from LDAP to AD ". |
||
| 421 | (count($msg) > $changed ? ' ('.(count($msg)-$changed).' errors!)' : ''); |
||
| 422 | } |
||
| 423 | |||
| 424 | /** |
||
| 425 | * Migrate to other account storage |
||
| 426 | * |
||
| 427 | * @param string $mode "passwords_to_sql", "migrate_to_(sql|ldap|univention)" |
||
| 428 | * @return string with success message |
||
| 429 | * @throws Exception on error |
||
| 430 | */ |
||
| 431 | private function migrate($mode) |
||
| 432 | { |
||
| 433 | // support old boolean mode |
||
| 434 | if (is_bool($mode)) $mode = $mode ? 'migrate_to_ldap' : 'migrate_to_sql'; |
||
| 435 | |||
| 436 | $passwords2sql = $mode === "passwords_to_sql"; |
||
| 437 | list(,$to) = explode('_to_', $mode); |
||
| 438 | |||
| 439 | $msg = array(); |
||
| 440 | // if migrating to ldap, check ldap and create context if not yet exiting |
||
| 441 | if ($to == 'ldap' && !empty($this->ldap_admin_pw)) |
||
| 442 | { |
||
| 443 | $msg[] = $this->create(); |
||
| 444 | } |
||
| 445 | elseif ($this->account_repository !== 'ads') |
||
| 446 | { |
||
| 447 | $msg[] = $this->connect(); |
||
| 448 | } |
||
| 449 | // read accounts from old store |
||
| 450 | $accounts = $this->accounts($to == 'sql' ? $this->account_repository : 'sql', $passwords2sql ? 'accounts' : 'both'); |
||
| 451 | |||
| 452 | // clean up SQL before migration |
||
| 453 | View Code Duplication | if ($to == 'sql' && $this->truncate_egw_accounts) |
|
| 454 | { |
||
| 455 | $GLOBALS['egw']->db->query('TRUNCATE TABLE egw_accounts', __LINE__, __FILE__); |
||
| 456 | $GLOBALS['egw']->db->query('DELETE FROM egw_addressbook WHERE account_id IS NOT NULL', __LINE__, __FILE__); |
||
| 457 | } |
||
| 458 | // instanciate accounts obj for new store |
||
| 459 | $accounts_obj = $this->accounts_obj($to); |
||
| 460 | |||
| 461 | $accounts_created = $groups_created = $errors = $egw_info_set = 0; |
||
| 462 | $emailadmin_src = $ldap_class = null; |
||
| 463 | $target = strtoupper($to); |
||
| 464 | foreach($accounts as $account_id => $account) |
||
| 465 | { |
||
| 466 | if (isset($this->only) && !in_array($account_id,$this->only)) |
||
| 467 | { |
||
| 468 | continue; |
||
| 469 | } |
||
| 470 | $what = ($account['account_type'] == 'u' ? lang('User') : lang('Group')).' '. |
||
| 471 | $account_id.' ('.$account['account_lid'].')'; |
||
| 472 | |||
| 473 | // if we migrate passwords from an authentication source, we need to use account_lid, not numerical id |
||
| 474 | if ($passwords2sql && ($id = $accounts_obj->name2id($account['account_lid'], 'account_lid', 'u'))) |
||
| 475 | { |
||
| 476 | $account_id = $id; |
||
| 477 | } |
||
| 478 | |||
| 479 | // invalidate cache: otherwise no migration takes place, if cached results says account already exists |
||
| 480 | Api\Accounts::cache_invalidate($account_id); |
||
| 481 | |||
| 482 | if ($passwords2sql) |
||
| 483 | { |
||
| 484 | if (!($sql_account = $accounts_obj->read($account_id))) |
||
| 485 | { |
||
| 486 | $msg[] = lang('%1 does NOT exist in %2.',$what,$target); |
||
| 487 | $errors++; |
||
| 488 | } |
||
| 489 | elseif(empty($account['account_pwd'])) |
||
| 490 | { |
||
| 491 | $msg[] = lang('%1 does NOT have a password (userPassword attribute) or we are not allowed to read it!',$what); |
||
| 492 | $errors++; |
||
| 493 | } |
||
| 494 | else |
||
| 495 | { |
||
| 496 | $sql_account['account_passwd'] = self::hash_ldap2sql($account['account_pwd']); |
||
| 497 | |||
| 498 | if (!$accounts_obj->save($sql_account)) |
||
| 499 | { |
||
| 500 | $msg[] = lang('Update of %1 in %2 failed !!!',$what,$target); |
||
| 501 | $errors++; |
||
| 502 | } |
||
| 503 | else |
||
| 504 | { |
||
| 505 | $msg[] = lang('%1 password set in %2.',$what,$target); |
||
| 506 | $accounts_created++; |
||
| 507 | } |
||
| 508 | } |
||
| 509 | continue; |
||
| 510 | } |
||
| 511 | |||
| 512 | if ($account['account_type'] == 'u') |
||
| 513 | { |
||
| 514 | if ($accounts_obj->exists($account_id)) |
||
| 515 | { |
||
| 516 | $msg[] = lang('%1 already exists in %2.',$what,$target); |
||
| 517 | $errors++; |
||
| 518 | continue; |
||
| 519 | } |
||
| 520 | if ($to != 'sql') |
||
| 521 | { |
||
| 522 | if ($GLOBALS['egw_info']['server']['ldap_extra_attributes']) |
||
| 523 | { |
||
| 524 | $account['homedirectory'] = $GLOBALS['egw_info']['server']['ldap_account_home'] . '/' . $account['account_lid']; |
||
| 525 | $account['loginshell'] = $GLOBALS['egw_info']['server']['ldap_account_shell']; |
||
| 526 | } |
||
| 527 | $account['account_passwd'] = self::hash_sql2ldap($account['account_pwd']); |
||
| 528 | } |
||
| 529 | else |
||
| 530 | { |
||
| 531 | $account['account_passwd'] = self::hash_ldap2sql($account['account_pwd']); |
||
| 532 | } |
||
| 533 | unset($account['person_id']); |
||
| 534 | |||
| 535 | View Code Duplication | if (!$accounts_obj->save($account)) |
|
| 536 | { |
||
| 537 | $msg[] = lang('Creation of %1 in %2 failed !!!',$what,$target); |
||
| 538 | $errors++; |
||
| 539 | continue; |
||
| 540 | } |
||
| 541 | $accounts_obj->set_memberships($account['memberships'],$account_id); |
||
| 542 | $msg[] = lang('%1 created in %2.',$what,$target); |
||
| 543 | $accounts_created++; |
||
| 544 | |||
| 545 | // check if we need to migrate mail-account |
||
| 546 | if (!isset($ldap_class) && $this->account_repository !== 'ads') |
||
| 547 | { |
||
| 548 | $ldap_class = false; |
||
| 549 | $ldap = Api\Ldap::factory(false); |
||
| 550 | foreach(array( // todo: have these enumerated by emailadmin ... |
||
| 551 | 'qmailUser' => 'EGroupware\\Api\\Mail\\Smtp\\Oldqmailuser', |
||
| 552 | 'dbMailUser' => 'EGroupware\\Api\\Mail\\Smtp\\Dbmailuser', |
||
| 553 | // nothing to migrate for inetOrgPerson ... |
||
| 554 | ) as $object_class => $class) |
||
| 555 | { |
||
| 556 | if ($ldap->getLDAPServerInfo()->supportsObjectClass($object_class)) |
||
| 557 | { |
||
| 558 | $ldap_class = $class; |
||
| 559 | break; |
||
| 560 | } |
||
| 561 | } |
||
| 562 | } |
||
| 563 | if ($ldap_class) |
||
| 564 | { |
||
| 565 | if (!isset($emailadmin_src)) |
||
| 566 | { |
||
| 567 | if ($to != 'sql') |
||
| 568 | { |
||
| 569 | $emailadmin_src = new Api\Mail\Smtp\Sql(); |
||
| 570 | $emailadmin_dst = new $ldap_class(); |
||
| 571 | } |
||
| 572 | else |
||
| 573 | { |
||
| 574 | $emailadmin_src = new $ldap_class(); |
||
| 575 | $emailadmin_dst = new Api\Mail\Smtp\Sql(); |
||
| 576 | } |
||
| 577 | } |
||
| 578 | if (($mailaccount = $emailadmin_src->getUserData($account_id))) |
||
| 579 | { |
||
| 580 | //echo "<p>".array2string($mailaccount).': '; |
||
| 581 | $emailadmin_dst->setUserData($account_id, (array)$mailaccount['mailAlternateAddress'], |
||
| 582 | (array)$mailaccount['mailForwardingAddress'], $mailaccount['deliveryMode'], |
||
| 583 | $mailaccount['accountStatus'], $mailaccount['mailLocalAddress'], |
||
| 584 | $mailaccount['quotaLimit'], false, $mailaccount['mailMessageStore']); |
||
| 585 | |||
| 586 | $msg[] = lang("Mail account of %1 migraged", $account['account_lid']); |
||
| 587 | } |
||
| 588 | //else echo "<p>No mail account data found for #$account_id $account[account_lid]!</p>\n"; |
||
| 589 | } |
||
| 590 | |||
| 591 | // should we run any or some addAccount hooks |
||
| 592 | if ($this->add_account_hook) |
||
| 593 | { |
||
| 594 | // setting up egw_info array with new ldap information, so hook can use Api\Ldap::ldapConnect() |
||
| 595 | if (!$egw_info_set++) |
||
| 596 | { |
||
| 597 | foreach(array('ldap_host','ldap_root_dn','ldap_root_pw','ldap_context','ldap_group_context','ldap_search_filter','ldap_encryptin_type','mail_suffix','mail_login_type') as $name) |
||
| 598 | { |
||
| 599 | if (!empty($this->$name)) $GLOBALS['egw_info']['server'][$name] = $this->$name; |
||
| 600 | } |
||
| 601 | //error_log(__METHOD__."() setup up egw_info[server]: ldap_host='{$GLOBALS['egw_info']['server']['ldap_host']}', ldap_root_dn='{$GLOBALS['egw_info']['server']['ldap_root_dn']}', ldap_root_pw='{$GLOBALS['egw_info']['server']['ldap_root_pw']}', ldap_context='{$GLOBALS['egw_info']['server']['ldap_context']}', mail_suffix='{$GLOBALS['egw_info']['server']['mail_suffix']}', mail_logig_type='{$GLOBALS['egw_info']['server']['mail_login-type']}'"); |
||
| 602 | } |
||
| 603 | try |
||
| 604 | { |
||
| 605 | $account['location'] = 'addAccount'; |
||
| 606 | // running all addAccount hooks (currently NOT working, as not all work in setup) |
||
| 607 | if ($this->add_account_hook === true) |
||
| 608 | { |
||
| 609 | Api\Hooks::process($account, array(), true); |
||
| 610 | } |
||
| 611 | elseif(is_callable($this->add_account_hook)) |
||
| 612 | { |
||
| 613 | call_user_func($this->add_account_hook,$account); |
||
| 614 | } |
||
| 615 | } |
||
| 616 | catch(Exception $e) |
||
| 617 | { |
||
| 618 | $msg[] = $e->getMessage(); |
||
| 619 | $errors++; |
||
| 620 | } |
||
| 621 | } |
||
| 622 | } |
||
| 623 | else |
||
| 624 | { |
||
| 625 | // check if group already exists |
||
| 626 | if (!$accounts_obj->exists($account_id)) |
||
| 627 | { |
||
| 628 | View Code Duplication | if (!$accounts_obj->save($account)) |
|
| 629 | { |
||
| 630 | $msg[] = lang('Creation of %1 in %2 failed !!!',$what,$target); |
||
| 631 | ++$errors; |
||
| 632 | continue; |
||
| 633 | } |
||
| 634 | $msg[] = lang('%1 created in %2.',$what,$target); |
||
| 635 | $groups_created++; |
||
| 636 | } |
||
| 637 | else |
||
| 638 | { |
||
| 639 | $msg[] = lang('%1 already exists in %2.',$what,$target); |
||
| 640 | $errors++; |
||
| 641 | |||
| 642 | if ($accounts_obj->id2name($account_id) != $account['account_lid']) |
||
| 643 | { |
||
| 644 | $msg[] = lang("==> different group '%1' under that gidNumber %2, NOT setting memberships!",$account['account_lid'],$account_id); |
||
| 645 | ++$errors; |
||
| 646 | continue; // different group under that gidnumber! |
||
| 647 | } |
||
| 648 | } |
||
| 649 | // now saving / updating the memberships |
||
| 650 | $accounts_obj->set_members($account['members'],$account_id); |
||
| 651 | } |
||
| 652 | } |
||
| 653 | if ($passwords2sql) |
||
| 654 | { |
||
| 655 | return lang('%1 passwords updated, %3 errors',$accounts_created,$groups_created,$errors). |
||
| 656 | ($errors || $this->verbose ? "\n- ".implode("\n- ",$msg) : ''); |
||
| 657 | } |
||
| 658 | // migrate addressbook data |
||
| 659 | $GLOBALS['egw_info']['user']['apps']['admin'] = true; // otherwise migration will not run in setup! |
||
| 660 | $addressbook = new Api\Contacts\Storage(); |
||
| 661 | foreach($this->as_array() as $name => $value) |
||
| 662 | { |
||
| 663 | if (substr($name, 5) == 'ldap_') |
||
| 664 | { |
||
| 665 | $GLOBALS['egw_info']['server'][$name] = $value; |
||
| 666 | } |
||
| 667 | } |
||
| 668 | ob_start(); |
||
| 669 | $addressbook->migrate2ldap($to != 'sql' ? 'accounts' : 'accounts-back'. |
||
| 670 | ($this->account_repository == 'ads' ? '-ads' : '')); |
||
| 671 | $msgs = array_merge($msg, explode("\n", strip_tags(ob_get_clean()))); |
||
| 672 | |||
| 673 | $this->restore_db(); |
||
| 674 | |||
| 675 | return lang('%1 users and %2 groups created, %3 errors',$accounts_created,$groups_created,$errors). |
||
| 676 | ($errors || $this->verbose ? "\n- ".implode("\n- ",$msgs) : ''); |
||
| 677 | } |
||
| 678 | |||
| 679 | /** |
||
| 680 | * Convert SQL hash to LDAP hash |
||
| 681 | * |
||
| 682 | * @param string $hash |
||
| 683 | * @return string |
||
| 684 | */ |
||
| 685 | public static function hash_sql2ldap($hash) |
||
| 712 | |||
| 713 | /** |
||
| 714 | * Convert LDAP hash to SQL hash |
||
| 715 | * |
||
| 716 | * @param string $hash |
||
| 717 | * @return string |
||
| 718 | */ |
||
| 719 | public static function hash_ldap2sql($hash) |
||
| 727 | |||
| 728 | /** |
||
| 729 | * Read all accounts from sql or ldap |
||
| 730 | * |
||
| 731 | * @param string $from ='ldap', 'ldap', 'sql', 'univention' |
||
| 732 | * @param string $type ='both' |
||
| 733 | * @return array |
||
| 734 | */ |
||
| 735 | public function accounts($from='ldap', $type='both') |
||
| 764 | |||
| 765 | /** |
||
| 766 | * Instanciate accounts object from either sql of ldap |
||
| 767 | * |
||
| 768 | * @param string $type 'ldap', 'sql', 'univention' |
||
| 769 | * @return Api\Accounts |
||
| 770 | */ |
||
| 771 | private function accounts_obj($type) |
||
| 772 | { |
||
| 773 | static $enviroment_setup=null; |
||
| 774 | if (!$enviroment_setup) |
||
| 775 | { |
||
| 776 | parent::_setup_enviroment($this->domain); |
||
| 777 | $enviroment_setup = true; |
||
| 778 | } |
||
| 779 | if ($type != 'sql' && $type != 'ads') $this->connect(); // throws exception, if it can NOT connect |
||
| 780 | |||
| 781 | // otherwise search does NOT work, as accounts_sql uses addressbook_bo for it |
||
| 782 | $GLOBALS['egw_info']['server']['account_repository'] = $type; |
||
| 783 | |||
| 784 | if (!self::$egw_setup->setup_account_object( |
||
| 785 | array( |
||
| 786 | 'account_repository' => $GLOBALS['egw_info']['server']['account_repository'], |
||
| 787 | ) + $this->as_array()) || |
||
| 788 | !is_a(self::$egw_setup->accounts, 'EGroupware\\Api\\Accounts') || |
||
| 789 | !is_a(self::$egw_setup->accounts->backend, 'EGroupware\\Api\\Accounts\\'.ucfirst($type))) |
||
| 790 | { |
||
| 791 | throw new Exception(lang("Can NOT instancate accounts object for %1", strtoupper($type))); |
||
| 792 | } |
||
| 793 | return self::$egw_setup->accounts; |
||
| 794 | } |
||
| 795 | |||
| 796 | /** |
||
| 797 | * Connect to ldap server |
||
| 798 | * |
||
| 799 | * @param string $dn =null default $this->ldap_root_dn |
||
| 800 | * @param string $pw =null default $this->ldap_root_pw |
||
| 801 | * @param string $host =null default $this->ldap_host, hostname, ip or ldap-url |
||
| 802 | * @throws Api\Exception\WrongUserinput Can not connect to ldap ... |
||
| 803 | */ |
||
| 804 | private function connect($dn=null,$pw=null,$host=null) |
||
| 805 | { |
||
| 806 | if (is_null($dn)) $dn = $this->ldap_root_dn; |
||
| 807 | if (is_null($pw)) $pw = $this->ldap_root_pw; |
||
| 808 | if (is_null($host)) $host = $this->ldap_host; |
||
| 809 | |||
| 810 | if (!$pw) // Api\Ldap::ldapConnect use the current eGW's pw otherwise |
||
| 811 | { |
||
| 812 | throw new Api\Exception\WrongUserinput(lang('You need to specify a password!')); |
||
| 813 | } |
||
| 814 | |||
| 815 | try { |
||
| 816 | $this->test_ldap = Api\Ldap::factory(false, $host, $dn, $pw); |
||
| 817 | } |
||
| 818 | catch (Api\Exception\NoPermission $e) { |
||
| 819 | _egw_log_exception($e); |
||
| 820 | throw new Api\Exception\WrongUserinput(lang('Can not connect to LDAP server on host %1 using DN %2!', |
||
| 821 | $host,$dn).($this->test_ldap->ds ? ' ('.ldap_error($this->test_ldap->ds).')' : '')); |
||
| 822 | } |
||
| 823 | return lang('Successful connected to LDAP server on %1 using DN %2.',$this->ldap_host,$dn); |
||
| 824 | } |
||
| 825 | |||
| 826 | /** |
||
| 827 | * Count active (not expired) users |
||
| 828 | * |
||
| 829 | * @return int number of active users |
||
| 830 | * @throws Api\Exception\WrongUserinput |
||
| 831 | */ |
||
| 832 | private function users() |
||
| 850 | |||
| 851 | /** |
||
| 852 | * Check and if does not yet exist create the new database and user |
||
| 853 | * |
||
| 854 | * @return string with success message |
||
| 855 | * @throws Api\Exception\WrongUserinput |
||
| 856 | */ |
||
| 857 | private function create() |
||
| 877 | |||
| 878 | /** |
||
| 879 | * Delete whole LDAP tree of an instance dn=$this->ldap_base using $this->ldap_admin/_pw |
||
| 880 | * |
||
| 881 | * @return string with success message |
||
| 882 | * @throws Api\Exception if dn not found, not listable or delete fails |
||
| 883 | */ |
||
| 884 | private function delete_base() |
||
| 906 | |||
| 907 | /** |
||
| 908 | * Recursive delete a dn |
||
| 909 | * |
||
| 910 | * @param string $dn |
||
| 911 | * @return int integer number of deleted entries |
||
| 912 | * @throws Api\Exception if dn not listable or delete fails |
||
| 913 | */ |
||
| 914 | private function rdelete($dn) |
||
| 933 | |||
| 934 | /** |
||
| 935 | * Set mailbox attribute in $this->ldap_base according to given format |
||
| 936 | * |
||
| 937 | * Uses $this->ldap_host, $this->ldap_admin and $this->ldap_admin_pw to connect. |
||
| 938 | * |
||
| 939 | * @param string $this->object_class ='qmailUser' |
||
| 940 | * @param string $this->mbox_attr ='mailmessagestore' lowercase!!! |
||
| 941 | * @param string $this->mail_login_type ='email' 'email', 'vmailmgr', 'standard' or 'uidNumber' |
||
| 942 | * @return string with success message N entries modified |
||
| 943 | * @throws Api\Exception if dn not found, not listable or delete fails |
||
| 944 | */ |
||
| 945 | private function set_mailbox($check_only=false) |
||
| 994 | |||
| 995 | /** |
||
| 996 | * array with objectclasses for the objects we can create |
||
| 997 | * |
||
| 998 | * @var array of name => objectClass pairs (or array with multiple) |
||
| 999 | */ |
||
| 1000 | static $requiredObjectclasses = array( |
||
| 1001 | 'o' => 'organization', |
||
| 1002 | 'ou' => 'organizationalUnit', |
||
| 1003 | 'cn' => array('organizationalRole','simpleSecurityObject'), |
||
| 1004 | 'uid' => array('uidObject','organizationalRole','simpleSecurityObject'), |
||
| 1005 | 'dc' => array('organization','dcObject'), |
||
| 1006 | ); |
||
| 1007 | |||
| 1008 | /** |
||
| 1009 | * Create a new node in the ldap tree |
||
| 1010 | * |
||
| 1011 | * @param string $dn dn to create, eg. "cn=admin,dc=local" |
||
| 1012 | * @param array $extra =array() extra attributes to set |
||
| 1013 | * @return boolean true if the node was create, false if it was already there |
||
| 1014 | * @throws Api\Exception\WrongUserinput |
||
| 1015 | */ |
||
| 1016 | private function _create_node($dn,$extra=array()) |
||
| 1050 | |||
| 1051 | /** |
||
| 1052 | * Return default database settings for a given domain |
||
| 1053 | * |
||
| 1054 | * @return array |
||
| 1055 | */ |
||
| 1056 | static function defaults() |
||
| 1071 | |||
| 1072 | /** |
||
| 1073 | * Merges the default into the current properties, if they are empty or contain placeholders |
||
| 1074 | */ |
||
| 1075 | private function _merge_defaults() |
||
| 1104 | } |
||
| 1105 |