[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 use dokuwiki\Utf8\Sort; 3 use dokuwiki\Logger; 4 5 /** 6 * Active Directory authentication backend for DokuWiki 7 * 8 * This makes authentication with a Active Directory server much easier 9 * than when using the normal LDAP backend by utilizing the adLDAP library 10 * 11 * Usage: 12 * Set DokuWiki's local.protected.php auth setting to read 13 * 14 * $conf['authtype'] = 'authad'; 15 * 16 * $conf['plugin']['authad']['account_suffix'] = '@my.domain.org'; 17 * $conf['plugin']['authad']['base_dn'] = 'DC=my,DC=domain,DC=org'; 18 * $conf['plugin']['authad']['domain_controllers'] = 'srv1.domain.org,srv2.domain.org'; 19 * 20 * //optional: 21 * $conf['plugin']['authad']['sso'] = 1; 22 * $conf['plugin']['authad']['admin_username'] = 'root'; 23 * $conf['plugin']['authad']['admin_password'] = 'pass'; 24 * $conf['plugin']['authad']['real_primarygroup'] = 1; 25 * $conf['plugin']['authad']['use_ssl'] = 1; 26 * $conf['plugin']['authad']['use_tls'] = 1; 27 * $conf['plugin']['authad']['debug'] = 1; 28 * // warn user about expiring password this many days in advance: 29 * $conf['plugin']['authad']['expirywarn'] = 5; 30 * 31 * // get additional information to the userinfo array 32 * // add a list of comma separated ldap contact fields. 33 * $conf['plugin']['authad']['additional'] = 'field1,field2'; 34 * 35 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 36 * @author James Van Lommel <jamesvl@gmail.com> 37 * @link http://www.nosq.com/blog/2005/08/ldap-activedirectory-and-dokuwiki/ 38 * @author Andreas Gohr <andi@splitbrain.org> 39 * @author Jan Schumann <js@schumann-it.com> 40 */ 41 class auth_plugin_authad extends DokuWiki_Auth_Plugin 42 { 43 44 /** 45 * @var array hold connection data for a specific AD domain 46 */ 47 protected $opts = array(); 48 49 /** 50 * @var array open connections for each AD domain, as adLDAP objects 51 */ 52 protected $adldap = array(); 53 54 /** 55 * @var bool message state 56 */ 57 protected $msgshown = false; 58 59 /** 60 * @var array user listing cache 61 */ 62 protected $users = array(); 63 64 /** 65 * @var array filter patterns for listing users 66 */ 67 protected $pattern = array(); 68 69 protected $grpsusers = array(); 70 71 /** 72 * Constructor 73 */ 74 public function __construct() 75 { 76 global $INPUT; 77 parent::__construct(); 78 79 require_once(DOKU_PLUGIN.'authad/adLDAP/adLDAP.php'); 80 require_once(DOKU_PLUGIN.'authad/adLDAP/classes/adLDAPUtils.php'); 81 82 // we load the config early to modify it a bit here 83 $this->loadConfig(); 84 85 // additional information fields 86 if (isset($this->conf['additional'])) { 87 $this->conf['additional'] = str_replace(' ', '', $this->conf['additional']); 88 $this->conf['additional'] = explode(',', $this->conf['additional']); 89 } else $this->conf['additional'] = array(); 90 91 // ldap extension is needed 92 if (!function_exists('ldap_connect')) { 93 if ($this->conf['debug']) 94 msg("AD Auth: PHP LDAP extension not found.", -1); 95 $this->success = false; 96 return; 97 } 98 99 // Prepare SSO 100 if (!empty($INPUT->server->str('REMOTE_USER'))) { 101 // make sure the right encoding is used 102 if ($this->getConf('sso_charset')) { 103 $INPUT->server->set('REMOTE_USER', 104 iconv($this->getConf('sso_charset'), 'UTF-8', $INPUT->server->str('REMOTE_USER'))); 105 } elseif (!\dokuwiki\Utf8\Clean::isUtf8($INPUT->server->str('REMOTE_USER'))) { 106 $INPUT->server->set('REMOTE_USER', utf8_encode($INPUT->server->str('REMOTE_USER'))); 107 } 108 109 // trust the incoming user 110 if ($this->conf['sso']) { 111 $INPUT->server->set('REMOTE_USER', $this->cleanUser($INPUT->server->str('REMOTE_USER'))); 112 113 // we need to simulate a login 114 if (empty($_COOKIE[DOKU_COOKIE])) { 115 $INPUT->set('u', $INPUT->server->str('REMOTE_USER')); 116 $INPUT->set('p', 'sso_only'); 117 } 118 } 119 } 120 121 // other can do's are changed in $this->_loadServerConfig() base on domain setup 122 $this->cando['modName'] = (bool)$this->conf['update_name']; 123 $this->cando['modMail'] = (bool)$this->conf['update_mail']; 124 $this->cando['getUserCount'] = true; 125 } 126 127 /** 128 * Load domain config on capability check 129 * 130 * @param string $cap 131 * @return bool 132 */ 133 public function canDo($cap) 134 { 135 global $INPUT; 136 //capabilities depend on config, which may change depending on domain 137 $domain = $this->getUserDomain($INPUT->server->str('REMOTE_USER')); 138 $this->loadServerConfig($domain); 139 return parent::canDo($cap); 140 } 141 142 /** 143 * Check user+password [required auth function] 144 * 145 * Checks if the given user exists and the given 146 * plaintext password is correct by trying to bind 147 * to the LDAP server 148 * 149 * @author James Van Lommel <james@nosq.com> 150 * @param string $user 151 * @param string $pass 152 * @return bool 153 */ 154 public function checkPass($user, $pass) 155 { 156 global $INPUT; 157 if ($INPUT->server->str('REMOTE_USER') == $user && 158 $this->conf['sso'] 159 ) return true; 160 161 $adldap = $this->initAdLdap($this->getUserDomain($user)); 162 if (!$adldap) return false; 163 164 try { 165 return $adldap->authenticate($this->getUserName($user), $pass); 166 } catch (adLDAPException $e) { 167 // shouldn't really happen 168 return false; 169 } 170 } 171 172 /** 173 * Return user info [required auth function] 174 * 175 * Returns info about the given user needs to contain 176 * at least these fields: 177 * 178 * name string full name of the user 179 * mail string email address of the user 180 * grps array list of groups the user is in 181 * 182 * This AD specific function returns the following 183 * addional fields: 184 * 185 * dn string distinguished name (DN) 186 * uid string samaccountname 187 * lastpwd int timestamp of the date when the password was set 188 * expires true if the password expires 189 * expiresin int seconds until the password expires 190 * any fields specified in the 'additional' config option 191 * 192 * @author James Van Lommel <james@nosq.com> 193 * @param string $user 194 * @param bool $requireGroups (optional) - ignored, groups are always supplied by this plugin 195 * @return array 196 */ 197 public function getUserData($user, $requireGroups = true) 198 { 199 global $conf; 200 global $lang; 201 global $ID; 202 global $INPUT; 203 $adldap = $this->initAdLdap($this->getUserDomain($user)); 204 if (!$adldap) return array(); 205 206 if ($user == '') return array(); 207 208 $fields = array('mail', 'displayname', 'samaccountname', 'lastpwd', 'pwdlastset', 'useraccountcontrol'); 209 210 // add additional fields to read 211 $fields = array_merge($fields, $this->conf['additional']); 212 $fields = array_unique($fields); 213 $fields = array_filter($fields); 214 215 //get info for given user 216 $result = $adldap->user()->info($this->getUserName($user), $fields); 217 if ($result == false) { 218 return array(); 219 } 220 221 //general user info 222 $info = array(); 223 $info['name'] = $result[0]['displayname'][0]; 224 $info['mail'] = $result[0]['mail'][0]; 225 $info['uid'] = $result[0]['samaccountname'][0]; 226 $info['dn'] = $result[0]['dn']; 227 //last password set (Windows counts from January 1st 1601) 228 $info['lastpwd'] = $result[0]['pwdlastset'][0] / 10000000 - 11644473600; 229 //will it expire? 230 $info['expires'] = !($result[0]['useraccountcontrol'][0] & 0x10000); //ADS_UF_DONT_EXPIRE_PASSWD 231 232 // additional information 233 foreach ($this->conf['additional'] as $field) { 234 if (isset($result[0][strtolower($field)])) { 235 $info[$field] = $result[0][strtolower($field)][0]; 236 } 237 } 238 239 // handle ActiveDirectory memberOf 240 $info['grps'] = $adldap->user()->groups($this->getUserName($user), (bool) $this->opts['recursive_groups']); 241 242 if (is_array($info['grps'])) { 243 foreach ($info['grps'] as $ndx => $group) { 244 $info['grps'][$ndx] = $this->cleanGroup($group); 245 } 246 } 247 248 // always add the default group to the list of groups 249 if (!is_array($info['grps']) || !in_array($conf['defaultgroup'], $info['grps'])) { 250 $info['grps'][] = $conf['defaultgroup']; 251 } 252 253 // add the user's domain to the groups 254 $domain = $this->getUserDomain($user); 255 if ($domain && !in_array("domain-$domain", (array) $info['grps'])) { 256 $info['grps'][] = $this->cleanGroup("domain-$domain"); 257 } 258 259 // check expiry time 260 if ($info['expires'] && $this->conf['expirywarn']) { 261 try { 262 $expiry = $adldap->user()->passwordExpiry($user); 263 if (is_array($expiry)) { 264 $info['expiresat'] = $expiry['expiryts']; 265 $info['expiresin'] = round(($info['expiresat'] - time())/(24*60*60)); 266 267 // if this is the current user, warn him (once per request only) 268 if (($INPUT->server->str('REMOTE_USER') == $user) && 269 ($info['expiresin'] <= $this->conf['expirywarn']) && 270 !$this->msgshown 271 ) { 272 $msg = sprintf($this->getLang('authpwdexpire'), $info['expiresin']); 273 if ($this->canDo('modPass')) { 274 $url = wl($ID, array('do'=> 'profile')); 275 $msg .= ' <a href="'.$url.'">'.$lang['btn_profile'].'</a>'; 276 } 277 msg($msg); 278 $this->msgshown = true; 279 } 280 } 281 } catch (adLDAPException $e) { 282 // ignore. should usually not happen 283 } 284 } 285 286 return $info; 287 } 288 289 /** 290 * Make AD group names usable by DokuWiki. 291 * 292 * Removes backslashes ('\'), pound signs ('#'), and converts spaces to underscores. 293 * 294 * @author James Van Lommel (jamesvl@gmail.com) 295 * @param string $group 296 * @return string 297 */ 298 public function cleanGroup($group) 299 { 300 $group = str_replace('\\', '', $group); 301 $group = str_replace('#', '', $group); 302 $group = preg_replace('[\s]', '_', $group); 303 $group = \dokuwiki\Utf8\PhpString::strtolower(trim($group)); 304 return $group; 305 } 306 307 /** 308 * Sanitize user names 309 * 310 * Normalizes domain parts, does not modify the user name itself (unlike cleanGroup) 311 * 312 * @author Andreas Gohr <gohr@cosmocode.de> 313 * @param string $user 314 * @return string 315 */ 316 public function cleanUser($user) 317 { 318 $domain = ''; 319 320 // get NTLM or Kerberos domain part 321 list($dom, $user) = sexplode('\\', $user, 2, ''); 322 if (!$user) $user = $dom; 323 if ($dom) $domain = $dom; 324 list($user, $dom) = sexplode('@', $user, 2, ''); 325 if ($dom) $domain = $dom; 326 327 // clean up both 328 $domain = \dokuwiki\Utf8\PhpString::strtolower(trim($domain)); 329 $user = \dokuwiki\Utf8\PhpString::strtolower(trim($user)); 330 331 // is this a known, valid domain or do we work without account suffix? if not discard 332 if ((!isset($this->conf[$domain]) || !is_array($this->conf[$domain])) && 333 $this->conf['account_suffix'] !== '') { 334 $domain = ''; 335 } 336 337 // reattach domain 338 if ($domain) $user = "$user@$domain"; 339 return $user; 340 } 341 342 /** 343 * Most values in LDAP are case-insensitive 344 * 345 * @return bool 346 */ 347 public function isCaseSensitive() 348 { 349 return false; 350 } 351 352 /** 353 * Create a Search-String useable by adLDAPUsers::all($includeDescription = false, $search = "*", $sorted = true) 354 * 355 * @param array $filter 356 * @return string 357 */ 358 protected function constructSearchString($filter) 359 { 360 if (!$filter) { 361 return '*'; 362 } 363 $adldapUtils = new adLDAPUtils($this->initAdLdap(null)); 364 $result = '*'; 365 if (isset($filter['name'])) { 366 $result .= ')(displayname=*' . $adldapUtils->ldapSlashes($filter['name']) . '*'; 367 unset($filter['name']); 368 } 369 370 if (isset($filter['user'])) { 371 $result .= ')(samAccountName=*' . $adldapUtils->ldapSlashes($filter['user']) . '*'; 372 unset($filter['user']); 373 } 374 375 if (isset($filter['mail'])) { 376 $result .= ')(mail=*' . $adldapUtils->ldapSlashes($filter['mail']) . '*'; 377 unset($filter['mail']); 378 } 379 return $result; 380 } 381 382 /** 383 * Return a count of the number of user which meet $filter criteria 384 * 385 * @param array $filter $filter array of field/pattern pairs, empty array for no filter 386 * @return int number of users 387 */ 388 public function getUserCount($filter = array()) 389 { 390 $adldap = $this->initAdLdap(null); 391 if (!$adldap) { 392 Logger::debug("authad/auth.php getUserCount(): _adldap not set."); 393 return -1; 394 } 395 if ($filter == array()) { 396 $result = $adldap->user()->all(); 397 } else { 398 $searchString = $this->constructSearchString($filter); 399 $result = $adldap->user()->all(false, $searchString); 400 if (isset($filter['grps'])) { 401 $this->users = array_fill_keys($result, false); 402 /** @var admin_plugin_usermanager $usermanager */ 403 $usermanager = plugin_load("admin", "usermanager", false); 404 $usermanager->setLastdisabled(true); 405 if (!isset($this->grpsusers[$this->filterToString($filter)])) { 406 $this->fillGroupUserArray($filter, $usermanager->getStart() + 3*$usermanager->getPagesize()); 407 } elseif (count($this->grpsusers[$this->filterToString($filter)]) < 408 $usermanager->getStart() + 3*$usermanager->getPagesize() 409 ) { 410 $this->fillGroupUserArray( 411 $filter, 412 $usermanager->getStart() + 413 3*$usermanager->getPagesize() - 414 count($this->grpsusers[$this->filterToString($filter)]) 415 ); 416 } 417 $result = $this->grpsusers[$this->filterToString($filter)]; 418 } else { 419 /** @var admin_plugin_usermanager $usermanager */ 420 $usermanager = plugin_load("admin", "usermanager", false); 421 $usermanager->setLastdisabled(false); 422 } 423 } 424 425 if (!$result) { 426 return 0; 427 } 428 return count($result); 429 } 430 431 /** 432 * 433 * create a unique string for each filter used with a group 434 * 435 * @param array $filter 436 * @return string 437 */ 438 protected function filterToString($filter) 439 { 440 $result = ''; 441 if (isset($filter['user'])) { 442 $result .= 'user-' . $filter['user']; 443 } 444 if (isset($filter['name'])) { 445 $result .= 'name-' . $filter['name']; 446 } 447 if (isset($filter['mail'])) { 448 $result .= 'mail-' . $filter['mail']; 449 } 450 if (isset($filter['grps'])) { 451 $result .= 'grps-' . $filter['grps']; 452 } 453 return $result; 454 } 455 456 /** 457 * Create an array of $numberOfAdds users passing a certain $filter, including belonging 458 * to a certain group and save them to a object-wide array. If the array 459 * already exists try to add $numberOfAdds further users to it. 460 * 461 * @param array $filter 462 * @param int $numberOfAdds additional number of users requested 463 * @return int number of Users actually add to Array 464 */ 465 protected function fillGroupUserArray($filter, $numberOfAdds) 466 { 467 if (isset($this->grpsusers[$this->filterToString($filter)])) { 468 $actualstart = count($this->grpsusers[$this->filterToString($filter)]); 469 } else { 470 $this->grpsusers[$this->filterToString($filter)] = []; 471 $actualstart = 0; 472 } 473 474 $i=0; 475 $count = 0; 476 $this->constructPattern($filter); 477 foreach ($this->users as $user => &$info) { 478 if ($i++ < $actualstart) { 479 continue; 480 } 481 if ($info === false) { 482 $info = $this->getUserData($user); 483 } 484 if ($this->filter($user, $info)) { 485 $this->grpsusers[$this->filterToString($filter)][$user] = $info; 486 if (($numberOfAdds > 0) && (++$count >= $numberOfAdds)) break; 487 } 488 } 489 return $count; 490 } 491 492 /** 493 * Bulk retrieval of user data 494 * 495 * @author Dominik Eckelmann <dokuwiki@cosmocode.de> 496 * 497 * @param int $start index of first user to be returned 498 * @param int $limit max number of users to be returned 499 * @param array $filter array of field/pattern pairs, null for no filter 500 * @return array userinfo (refer getUserData for internal userinfo details) 501 */ 502 public function retrieveUsers($start = 0, $limit = 0, $filter = array()) 503 { 504 $adldap = $this->initAdLdap(null); 505 if (!$adldap) return array(); 506 507 //if (!$this->users) { 508 //get info for given user 509 $result = $adldap->user()->all(false, $this->constructSearchString($filter)); 510 if (!$result) return array(); 511 $this->users = array_fill_keys($result, false); 512 //} 513 514 $i = 0; 515 $count = 0; 516 $result = array(); 517 518 if (!isset($filter['grps'])) { 519 /** @var admin_plugin_usermanager $usermanager */ 520 $usermanager = plugin_load("admin", "usermanager", false); 521 $usermanager->setLastdisabled(false); 522 $this->constructPattern($filter); 523 foreach ($this->users as $user => &$info) { 524 if ($i++ < $start) { 525 continue; 526 } 527 if ($info === false) { 528 $info = $this->getUserData($user); 529 } 530 $result[$user] = $info; 531 if (($limit > 0) && (++$count >= $limit)) break; 532 } 533 } else { 534 /** @var admin_plugin_usermanager $usermanager */ 535 $usermanager = plugin_load("admin", "usermanager", false); 536 $usermanager->setLastdisabled(true); 537 if (!isset($this->grpsusers[$this->filterToString($filter)]) || 538 count($this->grpsusers[$this->filterToString($filter)]) < ($start+$limit) 539 ) { 540 if(!isset($this->grpsusers[$this->filterToString($filter)])) { 541 $this->grpsusers[$this->filterToString($filter)] = []; 542 } 543 544 $this->fillGroupUserArray( 545 $filter, 546 $start+$limit - count($this->grpsusers[$this->filterToString($filter)]) +1 547 ); 548 } 549 if (!$this->grpsusers[$this->filterToString($filter)]) return array(); 550 foreach ($this->grpsusers[$this->filterToString($filter)] as $user => &$info) { 551 if ($i++ < $start) { 552 continue; 553 } 554 $result[$user] = $info; 555 if (($limit > 0) && (++$count >= $limit)) break; 556 } 557 } 558 return $result; 559 } 560 561 /** 562 * Modify user data 563 * 564 * @param string $user nick of the user to be changed 565 * @param array $changes array of field/value pairs to be changed 566 * @return bool 567 */ 568 public function modifyUser($user, $changes) 569 { 570 $return = true; 571 $adldap = $this->initAdLdap($this->getUserDomain($user)); 572 if (!$adldap) { 573 msg($this->getLang('connectfail'), -1); 574 return false; 575 } 576 577 // password changing 578 if (isset($changes['pass'])) { 579 try { 580 $return = $adldap->user()->password($this->getUserName($user), $changes['pass']); 581 } catch (adLDAPException $e) { 582 if ($this->conf['debug']) msg('AD Auth: '.$e->getMessage(), -1); 583 $return = false; 584 } 585 if (!$return) msg($this->getLang('passchangefail'), -1); 586 } 587 588 // changing user data 589 $adchanges = array(); 590 if (isset($changes['name'])) { 591 // get first and last name 592 $parts = explode(' ', $changes['name']); 593 $adchanges['surname'] = array_pop($parts); 594 $adchanges['firstname'] = join(' ', $parts); 595 $adchanges['display_name'] = $changes['name']; 596 } 597 if (isset($changes['mail'])) { 598 $adchanges['email'] = $changes['mail']; 599 } 600 if (count($adchanges)) { 601 try { 602 $return = $return & $adldap->user()->modify($this->getUserName($user), $adchanges); 603 } catch (adLDAPException $e) { 604 if ($this->conf['debug']) msg('AD Auth: '.$e->getMessage(), -1); 605 $return = false; 606 } 607 if (!$return) msg($this->getLang('userchangefail'), -1); 608 } 609 610 return $return; 611 } 612 613 /** 614 * Initialize the AdLDAP library and connect to the server 615 * 616 * When you pass null as domain, it will reuse any existing domain. 617 * Eg. the one of the logged in user. It falls back to the default 618 * domain if no current one is available. 619 * 620 * @param string|null $domain The AD domain to use 621 * @return adLDAP|bool true if a connection was established 622 */ 623 protected function initAdLdap($domain) 624 { 625 if (is_null($domain) && is_array($this->opts)) { 626 $domain = $this->opts['domain']; 627 } 628 629 $this->opts = $this->loadServerConfig((string) $domain); 630 if (isset($this->adldap[$domain])) return $this->adldap[$domain]; 631 632 // connect 633 try { 634 $this->adldap[$domain] = new adLDAP($this->opts); 635 return $this->adldap[$domain]; 636 } catch (Exception $e) { 637 if ($this->conf['debug']) { 638 msg('AD Auth: '.$e->getMessage(), -1); 639 } 640 $this->success = false; 641 $this->adldap[$domain] = null; 642 } 643 return false; 644 } 645 646 /** 647 * Get the domain part from a user 648 * 649 * @param string $user 650 * @return string 651 */ 652 public function getUserDomain($user) 653 { 654 list(, $domain) = sexplode('@', $user, 2, ''); 655 return $domain; 656 } 657 658 /** 659 * Get the user part from a user 660 * 661 * When an account suffix is set, we strip the domain part from the user 662 * 663 * @param string $user 664 * @return string 665 */ 666 public function getUserName($user) 667 { 668 if ($this->conf['account_suffix'] !== '') { 669 list($user) = explode('@', $user, 2); 670 } 671 return $user; 672 } 673 674 /** 675 * Fetch the configuration for the given AD domain 676 * 677 * @param string $domain current AD domain 678 * @return array 679 */ 680 protected function loadServerConfig($domain) 681 { 682 // prepare adLDAP standard configuration 683 $opts = $this->conf; 684 685 $opts['domain'] = $domain; 686 687 // add possible domain specific configuration 688 if ($domain && is_array($this->conf[$domain])) foreach ($this->conf[$domain] as $key => $val) { 689 $opts[$key] = $val; 690 } 691 692 // handle multiple AD servers 693 $opts['domain_controllers'] = explode(',', $opts['domain_controllers']); 694 $opts['domain_controllers'] = array_map('trim', $opts['domain_controllers']); 695 $opts['domain_controllers'] = array_filter($opts['domain_controllers']); 696 697 // compatibility with old option name 698 if (empty($opts['admin_username']) && !empty($opts['ad_username'])) { 699 $opts['admin_username'] = $opts['ad_username']; 700 } 701 if (empty($opts['admin_password']) && !empty($opts['ad_password'])) { 702 $opts['admin_password'] = $opts['ad_password']; 703 } 704 $opts['admin_password'] = conf_decodeString($opts['admin_password']); // deobfuscate 705 706 // we can change the password if SSL is set 707 if ($opts['update_pass'] && ($opts['use_ssl'] || $opts['use_tls'])) { 708 $this->cando['modPass'] = true; 709 } else { 710 $this->cando['modPass'] = false; 711 } 712 713 // adLDAP expects empty user/pass as NULL, we're less strict FS#2781 714 if (empty($opts['admin_username'])) $opts['admin_username'] = null; 715 if (empty($opts['admin_password'])) $opts['admin_password'] = null; 716 717 // user listing needs admin priviledges 718 if (!empty($opts['admin_username']) && !empty($opts['admin_password'])) { 719 $this->cando['getUsers'] = true; 720 } else { 721 $this->cando['getUsers'] = false; 722 } 723 724 return $opts; 725 } 726 727 /** 728 * Returns a list of configured domains 729 * 730 * The default domain has an empty string as key 731 * 732 * @return array associative array(key => domain) 733 */ 734 public function getConfiguredDomains() 735 { 736 $domains = array(); 737 if (empty($this->conf['account_suffix'])) return $domains; // not configured yet 738 739 // add default domain, using the name from account suffix 740 $domains[''] = ltrim($this->conf['account_suffix'], '@'); 741 742 // find additional domains 743 foreach ($this->conf as $key => $val) { 744 if (is_array($val) && isset($val['account_suffix'])) { 745 $domains[$key] = ltrim($val['account_suffix'], '@'); 746 } 747 } 748 Sort::ksort($domains); 749 750 return $domains; 751 } 752 753 /** 754 * Check provided user and userinfo for matching patterns 755 * 756 * The patterns are set up with $this->_constructPattern() 757 * 758 * @author Chris Smith <chris@jalakai.co.uk> 759 * 760 * @param string $user 761 * @param array $info 762 * @return bool 763 */ 764 protected function filter($user, $info) 765 { 766 foreach ($this->pattern as $item => $pattern) { 767 if ($item == 'user') { 768 if (!preg_match($pattern, $user)) return false; 769 } elseif ($item == 'grps') { 770 if (!count(preg_grep($pattern, $info['grps']))) return false; 771 } else { 772 if (!preg_match($pattern, $info[$item])) return false; 773 } 774 } 775 return true; 776 } 777 778 /** 779 * Create a pattern for $this->_filter() 780 * 781 * @author Chris Smith <chris@jalakai.co.uk> 782 * 783 * @param array $filter 784 */ 785 protected function constructPattern($filter) 786 { 787 $this->pattern = array(); 788 foreach ($filter as $item => $pattern) { 789 $this->pattern[$item] = '/'.str_replace('/', '\/', $pattern).'/i'; // allow regex characters 790 } 791 } 792 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body