From 7b0a749accedb4a6e1f6fb0115d59707f586ec9f Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 22 Aug 2012 12:58:49 +0200 Subject: Better support for multi domain AD setups This changes how the AD auth backend handles multiple domains. It is now possible to configure multiple authentication domains even when not using SSO. USers can provide a domain in NTLM- and Kerberos-Style (prepended with a backslash, appended with a @-char). IMPORTANT: If you used AD auth before, you will need to adjust your ACLs and $conf['superuser'] settings. This patch changes how user names are cleaned. Spaces and other special chars are no longer removed. The only adjustment is lowercasing the username and streamlining the domain handling. User's login names will now contain the domain name in Kerberos style (user@yourdomain.com) when they logged in a non-default domain. You need to make sure your ACLs are setup accordingly. Domain names are always lowercased and need to be specified lowercased in the config. --- inc/auth/ad.class.php | 320 +++++++++++++++++++++++++++++++------------------- 1 file changed, 196 insertions(+), 124 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index e161c2939..48f3b9247 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -42,11 +42,12 @@ require_once(DOKU_INC.'inc/adLDAP.php'); class auth_ad extends auth_basic { - var $cnf = null; - var $opts = null; - var $adldap = null; + var $cnf = array(); + var $opts = array(); + var $adldap = array(); var $users = null; var $msgshown = false; + var $_pattern = array(); /** * Constructor @@ -56,58 +57,34 @@ class auth_ad extends auth_basic { $this->cnf = $conf['auth']['ad']; // additional information fields - if (isset($this->cnf['additional'])) { + if(isset($this->cnf['additional'])) { $this->cnf['additional'] = str_replace(' ', '', $this->cnf['additional']); $this->cnf['additional'] = explode(',', $this->cnf['additional']); } else $this->cnf['additional'] = array(); // ldap extension is needed - if (!function_exists('ldap_connect')) { - if ($this->cnf['debug']) - msg("AD Auth: PHP LDAP extension not found.",-1); + if(!function_exists('ldap_connect')) { + if($this->cnf['debug']) + msg("AD Auth: PHP LDAP extension not found.", -1); $this->success = false; return; } // Prepare SSO - if(!utf8_check($_SERVER['REMOTE_USER'])){ + if(!utf8_check($_SERVER['REMOTE_USER'])) { $_SERVER['REMOTE_USER'] = utf8_encode($_SERVER['REMOTE_USER']); } - if($_SERVER['REMOTE_USER'] && $this->cnf['sso']){ - // remove possible NTLM domain - list($dom,$usr) = explode('\\',$_SERVER['REMOTE_USER'],2); - if(!$usr) $usr = $dom; - - // remove possible Kerberos domain - list($usr,$dom) = explode('@',$usr); - - $dom = strtolower($dom); - $_SERVER['REMOTE_USER'] = $usr; + if($_SERVER['REMOTE_USER'] && $this->cnf['sso']) { + $_SERVER['REMOTE_USER'] = $this->cleanUser($_SERVER['REMOTE_USER']); // we need to simulate a login - if(empty($_COOKIE[DOKU_COOKIE])){ + if(empty($_COOKIE[DOKU_COOKIE])) { $_REQUEST['u'] = $_SERVER['REMOTE_USER']; $_REQUEST['p'] = 'sso_only'; } } - // prepare adLDAP standard configuration - $this->opts = $this->cnf; - - // add possible domain specific configuration - if($dom && is_array($this->cnf[$dom])) foreach($this->cnf[$dom] as $key => $val){ - $this->opts[$key] = $val; - } - - // handle multiple AD servers - $this->opts['domain_controllers'] = explode(',',$this->opts['domain_controllers']); - $this->opts['domain_controllers'] = array_map('trim',$this->opts['domain_controllers']); - $this->opts['domain_controllers'] = array_filter($this->opts['domain_controllers']); - - // we can change the password if SSL is set - if($this->opts['use_ssl'] || $this->opts['use_tls']){ - $this->cando['modPass'] = true; - } + // other can do's are changed in $this->_loadServerConfig() base on domain setup $this->cando['modName'] = true; $this->cando['modMail'] = true; } @@ -120,15 +97,19 @@ class auth_ad extends auth_basic { * to the LDAP server * * @author James Van Lommel + * @param string $user + * @param string $pass * @return bool */ - function checkPass($user, $pass){ + function checkPass($user, $pass) { if($_SERVER['REMOTE_USER'] && - $_SERVER['REMOTE_USER'] == $user && - $this->cnf['sso']) return true; + $_SERVER['REMOTE_USER'] == $user && + $this->cnf['sso'] + ) return true; - if(!$this->_init()) return false; - return $this->adldap->authenticate($user, $pass); + $adldap = $this->_adldap($this->_userDomain($user)); + if(!$adldap) return false; + return $adldap->authenticate($user, $pass); } /** @@ -137,85 +118,92 @@ class auth_ad extends auth_basic { * Returns info about the given user needs to contain * at least these fields: * - * name string full name of the user - * mail string email address of the user - * grps array list of groups the user is in + * name string full name of the user + * mail string email address of the user + * grps array list of groups the user is in * - * This LDAP specific function returns the following + * This AD specific function returns the following * addional fields: * - * dn string distinguished name (DN) - * uid string Posix User ID + * dn string distinguished name (DN) + * uid string samaccountname + * lastpwd int timestamp of the date when the password was set + * expires true if the password expires + * expiresin int seconds until the password expires + * any fields specified in the 'additional' config option * * @author James Van Lommel + * @param string $user + * @return array */ - function getUserData($user){ + function getUserData($user) { global $conf; global $lang; global $ID; - if(!$this->_init()) return false; + $adldap = $this->_adldap($this->_userDomain($user)); + if(!$adldap) return false; if($user == '') return array(); - $fields = array('mail','displayname','samaccountname','lastpwd','pwdlastset','useraccountcontrol'); + $fields = array('mail', 'displayname', 'samaccountname', 'lastpwd', 'pwdlastset', 'useraccountcontrol'); // add additional fields to read $fields = array_merge($fields, $this->cnf['additional']); $fields = array_unique($fields); //get info for given user - $result = $this->adldap->user_info($user, $fields); - if($result == false){ + $result = $adldap->user_info($user, $fields); + if($result == false) { return array(); } //general user info - $info['name'] = $result[0]['displayname'][0]; - $info['mail'] = $result[0]['mail'][0]; - $info['uid'] = $result[0]['samaccountname'][0]; - $info['dn'] = $result[0]['dn']; + $info['name'] = $result[0]['displayname'][0]; + $info['mail'] = $result[0]['mail'][0]; + $info['uid'] = $result[0]['samaccountname'][0]; + $info['dn'] = $result[0]['dn']; //last password set (Windows counts from January 1st 1601) $info['lastpwd'] = $result[0]['pwdlastset'][0] / 10000000 - 11644473600; //will it expire? $info['expires'] = !($result[0]['useraccountcontrol'][0] & 0x10000); //ADS_UF_DONT_EXPIRE_PASSWD // additional information - foreach ($this->cnf['additional'] as $field) { - if (isset($result[0][strtolower($field)])) { + foreach($this->cnf['additional'] as $field) { + if(isset($result[0][strtolower($field)])) { $info[$field] = $result[0][strtolower($field)][0]; } } // handle ActiveDirectory memberOf - $info['grps'] = $this->adldap->user_groups($user,(bool) $this->opts['recursive_groups']); + $info['grps'] = $adldap->user_groups($user, (bool) $this->opts['recursive_groups']); - if (is_array($info['grps'])) { - foreach ($info['grps'] as $ndx => $group) { + if(is_array($info['grps'])) { + foreach($info['grps'] as $ndx => $group) { $info['grps'][$ndx] = $this->cleanGroup($group); } } // always add the default group to the list of groups - if(!is_array($info['grps']) || !in_array($conf['defaultgroup'],$info['grps'])){ + if(!is_array($info['grps']) || !in_array($conf['defaultgroup'], $info['grps'])) { $info['grps'][] = $conf['defaultgroup']; } // check expiry time - if($info['expires'] && $this->cnf['expirywarn']){ - $result = $this->adldap->domain_info(array('maxpwdage')); // maximum pass age - $maxage = -1 * $result['maxpwdage'][0] / 10000000; // negative 100 nanosecs - $timeleft = $maxage - (time() - $info['lastpwd']); - $timeleft = round($timeleft/(24*60*60)); + if($info['expires'] && $this->cnf['expirywarn']) { + $result = $adldap->domain_info(array('maxpwdage')); // maximum pass age + $maxage = -1 * $result['maxpwdage'][0] / 10000000; // negative 100 nanosecs + $timeleft = $maxage - (time() - $info['lastpwd']); + $timeleft = round($timeleft / (24 * 60 * 60)); $info['expiresin'] = $timeleft; // if this is the current user, warn him (once per request only) - if( ($_SERVER['REMOTE_USER'] == $user) && + if(($_SERVER['REMOTE_USER'] == $user) && ($timeleft <= $this->cnf['expirywarn']) && !$this->msgshown - ){ - $msg = sprintf($lang['authpwdexpire'],$timeleft); - if($this->canDo('modPass')){ - $url = wl($ID,array('do'=>'profile')); + ) { + $msg = sprintf($lang['authpwdexpire'], $timeleft); + if($this->canDo('modPass')) { + $url = wl($ID, array('do'=> 'profile')); $msg .= ' '.$lang['btn_profile'].''; } msg($msg); @@ -242,15 +230,38 @@ class auth_ad extends auth_basic { /** * Sanitize user names + * + * Normalizes domain parts, does not modify the user name itself (unlike cleanGroup) + * + * @author Andreas Gohr + * @param string $name + * @return string */ function cleanUser($name) { - return $this->cleanGroup($name); + // get NTLM or Kerberos domain part + list($dom, $name) = explode('\\', $name, 2); + if(!$name) $name = $dom; + list($name, $dom) = explode('@', $name, 2); + + // clean up both + $dom = strtolower(trim($dom)); + $name = strtolower(trim($name)); + + // is this a known, valid domain? if not discard + if(!is_array($this->cnf[$dom])) { + $dom = ''; + } + + // reattach domain + if($dom) $name = "$name@$dom"; + + return $name; } /** * Most values in LDAP are case-insensitive */ - function isCaseSensitive(){ + function isCaseSensitive() { return false; } @@ -258,36 +269,37 @@ class auth_ad extends auth_basic { * Bulk retrieval of user data * * @author Dominik Eckelmann - * @param start index of first user to be returned - * @param limit max number of users to be returned - * @param filter array of field/pattern pairs, null for no filter - * @return array of userinfo (refer getUserData for internal userinfo details) + * @param int $start index of first user to be returned + * @param int $limit max number of users to be returned + * @param array $filter array of field/pattern pairs, null for no filter + * @return array userinfo (refer getUserData for internal userinfo details) */ - function retrieveUsers($start=0,$limit=-1,$filter=array()) { - if(!$this->_init()) return false; + function retrieveUsers($start = 0, $limit = -1, $filter = array()) { + $adldap = $this->_adldap(null); + if(!$adldap) return false; - if ($this->users === null) { + if($this->users === null) { //get info for given user - $result = $this->adldap->all_users(); - if (!$result) return array(); + $result = $adldap->all_users(); + if(!$result) return array(); $this->users = array_fill_keys($result, false); } - $i = 0; + $i = 0; $count = 0; $this->_constructPattern($filter); $result = array(); - foreach ($this->users as $user => &$info) { - if ($i++ < $start) { + foreach($this->users as $user => &$info) { + if($i++ < $start) { continue; } - if ($info === false) { + if($info === false) { $info = $this->getUserData($user); } - if ($this->_filter($user, $info)) { + if($this->_filter($user, $info)) { $result[$user] = $info; - if (($limit >= 0) && (++$count >= $limit)) break; + if(($limit >= 0) && (++$count >= $limit)) break; } } return $result; @@ -296,41 +308,43 @@ class auth_ad extends auth_basic { /** * Modify user data * - * @param $user nick of the user to be changed - * @param $changes array of field/value pairs to be changed + * @param string $user nick of the user to be changed + * @param array $changes array of field/value pairs to be changed * @return bool */ function modifyUser($user, $changes) { $return = true; + $adldap = $this->_adldap($this->_userDomain($user)); + if(!$adldap) return false; // password changing - if(isset($changes['pass'])){ + if(isset($changes['pass'])) { try { - $return = $this->adldap->user_password($user,$changes['pass']); - } catch (adLDAPException $e) { - if ($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); + $return = $adldap->user_password($user, $changes['pass']); + } catch(adLDAPException $e) { + if($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); $return = false; } - if(!$return) msg('AD Auth: failed to change the password. Maybe the password policy was not met?',-1); + if(!$return) msg('AD Auth: failed to change the password. Maybe the password policy was not met?', -1); } // changing user data $adchanges = array(); - if(isset($changes['name'])){ + if(isset($changes['name'])) { // get first and last name - $parts = explode(' ',$changes['name']); - $adchanges['surname'] = array_pop($parts); - $adchanges['firstname'] = join(' ',$parts); + $parts = explode(' ', $changes['name']); + $adchanges['surname'] = array_pop($parts); + $adchanges['firstname'] = join(' ', $parts); $adchanges['display_name'] = $changes['name']; } - if(isset($changes['mail'])){ + if(isset($changes['mail'])) { $adchanges['email'] = $changes['mail']; } - if(count($adchanges)){ + if(count($adchanges)) { try { - $return = $return & $this->adldap->user_modify($user,$adchanges); - } catch (adLDAPException $e) { - if ($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); + $return = $return & $adldap->user_modify($user, $adchanges); + } catch(adLDAPException $e) { + if($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); $return = false; } } @@ -340,40 +354,98 @@ class auth_ad extends auth_basic { /** * Initialize the AdLDAP library and connect to the server + * + * When you pass null as domain, it will reuse any existing domain. + * Eg. the one of the logged in user. It falls back to the default + * domain if no current one is available. + * + * @param string|null $domain The AD domain to use + * @return adLDAP|bool true if a connection was established */ - function _init(){ - if(!is_null($this->adldap)) return true; + protected function _adldap($domain) { + if(is_null($domain) && is_array($this->opts)) { + $domain = $this->opts['domain']; + } + + $this->opts = $this->_loadServerConfig((string) $domain); + if(isset($this->adldap[$domain])) return $this->adldap[$domain]; // connect try { - $this->adldap = new adLDAP($this->opts); - if (isset($this->opts['ad_username']) && isset($this->opts['ad_password'])) { - $this->canDo['getUsers'] = true; - } - return true; - } catch (adLDAPException $e) { - if ($this->cnf['debug']) { + $this->adldap[$domain] = new adLDAP($this->opts); + return $this->adldap[$domain]; + } catch(adLDAPException $e) { + if($this->cnf['debug']) { msg('AD Auth: '.$e->getMessage(), -1); } - $this->success = false; - $this->adldap = null; + $this->success = false; + $this->adldap[$domain] = null; } return false; } + /** + * Get the domain part from a user + * + * @param $user + * @return string + */ + protected function _userDomain($user) { + list(, $domain) = explode('@', $user, 2); + return $domain; + } + + /** + * Fetch the configuration for the given AD domain + * + * @param string $domain current AD domain + * @return array + */ + protected function _loadServerConfig($domain) { + // prepare adLDAP standard configuration + $opts = $this->cnf; + + $opts['domain'] = $domain; + + // add possible domain specific configuration + if($domain && is_array($this->cnf[$domain])) foreach($this->cnf[$domain] as $key => $val) { + $opts[$key] = $val; + } + + // handle multiple AD servers + $opts['domain_controllers'] = explode(',', $opts['domain_controllers']); + $opts['domain_controllers'] = array_map('trim', $opts['domain_controllers']); + $opts['domain_controllers'] = array_filter($opts['domain_controllers']); + + // we can change the password if SSL is set + if($opts['use_ssl'] || $opts['use_tls']) { + $this->cando['modPass'] = true; + } else { + $this->cando['modPass'] = false; + } + + if(isset($opts['ad_username']) && isset($opts['ad_password'])) { + $this->cando['getUsers'] = true; + } else { + $this->cando['getUsers'] = true; + } + + return $opts; + } + /** * return 1 if $user + $info match $filter criteria, 0 otherwise * * @author Chris Smith */ function _filter($user, $info) { - foreach ($this->_pattern as $item => $pattern) { - if ($item == 'user') { - if (!preg_match($pattern, $user)) return 0; - } else if ($item == 'grps') { - if (!count(preg_grep($pattern, $info['grps']))) return 0; + foreach($this->_pattern as $item => $pattern) { + if($item == 'user') { + if(!preg_match($pattern, $user)) return 0; + } else if($item == 'grps') { + if(!count(preg_grep($pattern, $info['grps']))) return 0; } else { - if (!preg_match($pattern, $info[$item])) return 0; + if(!preg_match($pattern, $info[$item])) return 0; } } return 1; @@ -381,8 +453,8 @@ class auth_ad extends auth_basic { function _constructPattern($filter) { $this->_pattern = array(); - foreach ($filter as $item => $pattern) { - $this->_pattern[$item] = '/'.str_replace('/','\/',$pattern).'/i'; // allow regex characters + foreach($filter as $item => $pattern) { + $this->_pattern[$item] = '/'.str_replace('/', '\/', $pattern).'/i'; // allow regex characters } } } -- cgit v1.2.3 From e12ea8465c640702902f5a4d95793e0e980ca123 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 22 Aug 2012 13:07:23 +0200 Subject: use UTF-8 aware lower casing --- inc/auth/ad.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index 48f3b9247..674726a7e 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -244,8 +244,8 @@ class auth_ad extends auth_basic { list($name, $dom) = explode('@', $name, 2); // clean up both - $dom = strtolower(trim($dom)); - $name = strtolower(trim($name)); + $dom = utf8_strtolower(trim($dom)); + $name = utf8_strtolower(trim($name)); // is this a known, valid domain? if not discard if(!is_array($this->cnf[$dom])) { -- cgit v1.2.3 From 33c5220c3b4d37bf0906689a301c48568195141d Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 22 Aug 2012 13:19:50 +0200 Subject: some code cleanup/PHP5ization --- inc/auth/ad.class.php | 91 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 27 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index 674726a7e..363abe114 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -14,7 +14,8 @@ * $conf['authtype'] = 'ad'; * $conf['passcrypt'] = 'ssha'; * - * $conf['auth']['ad']['account_suffix'] = '@my.domain.org'; + * $conf['auth']['ad']['account_suffix'] = ' + * @my.domain.org'; * $conf['auth']['ad']['base_dn'] = 'DC=my,DC=domain,DC=org'; * $conf['auth']['ad']['domain_controllers'] = 'srv1.domain.org,srv2.domain.org'; * @@ -42,17 +43,38 @@ require_once(DOKU_INC.'inc/adLDAP.php'); class auth_ad extends auth_basic { - var $cnf = array(); - var $opts = array(); - var $adldap = array(); - var $users = null; - var $msgshown = false; - var $_pattern = array(); + /** + * @var array copy of the auth backend configuration + */ + protected $cnf = array(); + /** + * @var array hold connection data for a specific AD domain + */ + protected $opts = array(); + /** + * @var array open connections for each AD domain, as adLDAP objects + */ + protected $adldap = array(); + + /** + * @var bool message state + */ + protected $msgshown = false; + + /** + * @var array user listing cache + */ + protected $users = array(); + + /** + * @var array filter patterns for listing users + */ + protected $_pattern = array(); /** * Constructor */ - function __construct() { + public function __construct() { global $conf; $this->cnf = $conf['auth']['ad']; @@ -101,7 +123,7 @@ class auth_ad extends auth_basic { * @param string $pass * @return bool */ - function checkPass($user, $pass) { + public function checkPass($user, $pass) { if($_SERVER['REMOTE_USER'] && $_SERVER['REMOTE_USER'] == $user && $this->cnf['sso'] @@ -136,7 +158,7 @@ class auth_ad extends auth_basic { * @param string $user * @return array */ - function getUserData($user) { + public function getUserData($user) { global $conf; global $lang; global $ID; @@ -220,12 +242,14 @@ class auth_ad extends auth_basic { * Removes backslashes ('\'), pound signs ('#'), and converts spaces to underscores. * * @author James Van Lommel (jamesvl@gmail.com) + * @param string $group + * @return string */ - function cleanGroup($name) { - $sName = str_replace('\\', '', $name); - $sName = str_replace('#', '', $sName); - $sName = preg_replace('[\s]', '_', $sName); - return $sName; + public function cleanGroup($group) { + $group = str_replace('\\', '', $group); + $group = str_replace('#', '', $group); + $group = preg_replace('[\s]', '_', $group); + return $group; } /** @@ -237,7 +261,7 @@ class auth_ad extends auth_basic { * @param string $name * @return string */ - function cleanUser($name) { + public function cleanUser($name) { // get NTLM or Kerberos domain part list($dom, $name) = explode('\\', $name, 2); if(!$name) $name = $dom; @@ -260,8 +284,10 @@ class auth_ad extends auth_basic { /** * Most values in LDAP are case-insensitive + * + * @return bool */ - function isCaseSensitive() { + public function isCaseSensitive() { return false; } @@ -274,7 +300,7 @@ class auth_ad extends auth_basic { * @param array $filter array of field/pattern pairs, null for no filter * @return array userinfo (refer getUserData for internal userinfo details) */ - function retrieveUsers($start = 0, $limit = -1, $filter = array()) { + public function retrieveUsers($start = 0, $limit = -1, $filter = array()) { $adldap = $this->_adldap(null); if(!$adldap) return false; @@ -312,7 +338,7 @@ class auth_ad extends auth_basic { * @param array $changes array of field/value pairs to be changed * @return bool */ - function modifyUser($user, $changes) { + public function modifyUser($user, $changes) { $return = true; $adldap = $this->_adldap($this->_userDomain($user)); if(!$adldap) return false; @@ -434,24 +460,35 @@ class auth_ad extends auth_basic { } /** - * return 1 if $user + $info match $filter criteria, 0 otherwise + * Check provided user and userinfo for matching patterns * - * @author Chris Smith + * The patterns are set up with $this->_constructPattern() + * + * @author Chris Smith + * @param string $user + * @param array $info + * @return bool */ - function _filter($user, $info) { + protected function _filter($user, $info) { foreach($this->_pattern as $item => $pattern) { if($item == 'user') { - if(!preg_match($pattern, $user)) return 0; + if(!preg_match($pattern, $user)) return false; } else if($item == 'grps') { - if(!count(preg_grep($pattern, $info['grps']))) return 0; + if(!count(preg_grep($pattern, $info['grps']))) return false; } else { - if(!preg_match($pattern, $info[$item])) return 0; + if(!preg_match($pattern, $info[$item])) return false; } } - return 1; + return true; } - function _constructPattern($filter) { + /** + * Create a pattern for $this->_filter() + * + * @author Chris Smith + * @param array $filter + */ + protected function _constructPattern($filter) { $this->_pattern = array(); foreach($filter as $item => $pattern) { $this->_pattern[$item] = '/'.str_replace('/', '\/', $pattern).'/i'; // allow regex characters -- cgit v1.2.3 From 70d71ed8159c719aa35363d05cc1dea2a95144f0 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 22 Aug 2012 15:46:17 +0200 Subject: fixed domain handling in user names --- inc/auth/ad.class.php | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index 363abe114..e3ea87654 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -131,7 +131,8 @@ class auth_ad extends auth_basic { $adldap = $this->_adldap($this->_userDomain($user)); if(!$adldap) return false; - return $adldap->authenticate($user, $pass); + + return $adldap->authenticate($this->_userName($user), $pass); } /** @@ -174,7 +175,7 @@ class auth_ad extends auth_basic { $fields = array_unique($fields); //get info for given user - $result = $adldap->user_info($user, $fields); + $result = $adldap->user_info($this->_userName($user), $fields); if($result == false) { return array(); } @@ -197,7 +198,7 @@ class auth_ad extends auth_basic { } // handle ActiveDirectory memberOf - $info['grps'] = $adldap->user_groups($user, (bool) $this->opts['recursive_groups']); + $info['grps'] = $adldap->user_groups($this->_userName($user), (bool) $this->opts['recursive_groups']); if(is_array($info['grps'])) { foreach($info['grps'] as $ndx => $group) { @@ -258,28 +259,29 @@ class auth_ad extends auth_basic { * Normalizes domain parts, does not modify the user name itself (unlike cleanGroup) * * @author Andreas Gohr - * @param string $name + * @param string $user * @return string */ - public function cleanUser($name) { + public function cleanUser($user) { // get NTLM or Kerberos domain part - list($dom, $name) = explode('\\', $name, 2); - if(!$name) $name = $dom; - list($name, $dom) = explode('@', $name, 2); + list($dom, $user) = explode('\\', $user, 2); + if(!$user) $user = $dom; + if($dom) $domain = $dom; + list($user, $dom) = explode('@', $user, 2); + if($dom) $domain = $dom; // clean up both - $dom = utf8_strtolower(trim($dom)); - $name = utf8_strtolower(trim($name)); + $domain = utf8_strtolower(trim($domain)); + $user = utf8_strtolower(trim($user)); // is this a known, valid domain? if not discard - if(!is_array($this->cnf[$dom])) { - $dom = ''; + if(!is_array($this->cnf[$domain])) { + $domain = ''; } // reattach domain - if($dom) $name = "$name@$dom"; - - return $name; + if($domain) $user = "$user@$domain"; + return $user; } /** @@ -346,7 +348,7 @@ class auth_ad extends auth_basic { // password changing if(isset($changes['pass'])) { try { - $return = $adldap->user_password($user, $changes['pass']); + $return = $adldap->user_password($this->_userName($user), $changes['pass']); } catch(adLDAPException $e) { if($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); $return = false; @@ -368,7 +370,7 @@ class auth_ad extends auth_basic { } if(count($adchanges)) { try { - $return = $return & $adldap->user_modify($user, $adchanges); + $return = $return & $adldap->user_modify($this->_userName($user), $adchanges); } catch(adLDAPException $e) { if($this->cnf['debug']) msg('AD Auth: '.$e->getMessage(), -1); $return = false; @@ -421,6 +423,17 @@ class auth_ad extends auth_basic { return $domain; } + /** + * Get the user part from a user + * + * @param $user + * @return string + */ + protected function _userName($user) { + list($name) = explode('@', $user, 2); + return $name; + } + /** * Fetch the configuration for the given AD domain * -- cgit v1.2.3 From 86bd5c0df3ca48ea055bb3a47cc25a9c3eff23c3 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Wed, 22 Aug 2012 15:49:30 +0200 Subject: add user's domain to the list of groups --- inc/auth/ad.class.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index e3ea87654..76b8924aa 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -15,6 +15,7 @@ * $conf['passcrypt'] = 'ssha'; * * $conf['auth']['ad']['account_suffix'] = ' + * * @my.domain.org'; * $conf['auth']['ad']['base_dn'] = 'DC=my,DC=domain,DC=org'; * $conf['auth']['ad']['domain_controllers'] = 'srv1.domain.org,srv2.domain.org'; @@ -211,6 +212,12 @@ class auth_ad extends auth_basic { $info['grps'][] = $conf['defaultgroup']; } + // add the user's domain to the groups + $domain = $this->_userDomain($user); + if($domain && !in_array("domain-$domain", (array) $info['grps'])) { + $info['grps'][] = $this->cleanGroup("domain-$domain"); + } + // check expiry time if($info['expires'] && $this->cnf['expirywarn']) { $result = $adldap->domain_info(array('maxpwdage')); // maximum pass age @@ -263,6 +270,8 @@ class auth_ad extends auth_basic { * @return string */ public function cleanUser($user) { + $domain = ''; + // get NTLM or Kerberos domain part list($dom, $user) = explode('\\', $user, 2); if(!$user) $user = $dom; @@ -271,8 +280,8 @@ class auth_ad extends auth_basic { if($dom) $domain = $dom; // clean up both - $domain = utf8_strtolower(trim($domain)); - $user = utf8_strtolower(trim($user)); + $domain = utf8_strtolower(trim($domain)); + $user = utf8_strtolower(trim($user)); // is this a known, valid domain? if not discard if(!is_array($this->cnf[$domain])) { -- cgit v1.2.3 From 496e18cda1b159f7e70345a4ee6204d80d815145 Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 23 Aug 2012 11:33:39 +0200 Subject: make AD utilitiy functions public --- inc/auth/ad.class.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index 76b8924aa..7919c71ac 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -427,7 +427,7 @@ class auth_ad extends auth_basic { * @param $user * @return string */ - protected function _userDomain($user) { + public function _userDomain($user) { list(, $domain) = explode('@', $user, 2); return $domain; } @@ -438,7 +438,7 @@ class auth_ad extends auth_basic { * @param $user * @return string */ - protected function _userName($user) { + public function _userName($user) { list($name) = explode('@', $user, 2); return $name; } -- cgit v1.2.3 From 006b89daf7868db73e9a046a3804e8e60345ab4c Mon Sep 17 00:00:00 2001 From: Andreas Gohr Date: Thu, 23 Aug 2012 13:55:54 +0200 Subject: lowercase groups --- inc/auth/ad.class.php | 1 + 1 file changed, 1 insertion(+) (limited to 'inc/auth') diff --git a/inc/auth/ad.class.php b/inc/auth/ad.class.php index 7919c71ac..f535556a8 100644 --- a/inc/auth/ad.class.php +++ b/inc/auth/ad.class.php @@ -257,6 +257,7 @@ class auth_ad extends auth_basic { $group = str_replace('\\', '', $group); $group = str_replace('#', '', $group); $group = preg_replace('[\s]', '_', $group); + $group = utf8_strtolower(trim($group)); return $group; } -- cgit v1.2.3