array()); is used in config.php. // Note 2: search is likely to not work properly with OpenLDAP as well because of Windows specific filtering code in there // (we are looking for matches for Windows-specific samaccountname property). Search needs to be redone during the next // refactoring effort. /** * Auth_ldap class to authenticate users against an LDAP server (Windows AD, OpenLDAP, and others). * @package TimeTracker */ class Auth_ldap extends Auth { var $params; function __construct($params) { global $smarty; $this->params = $params; $smarty->assign('Auth_ldap_params', $this->params); } function ldap_escape($str){ $illegal = array("(", ")", "#"); $legal = array(); foreach ($illegal as $id => $char) { $legal[$id] = "\\".$char; } $str = str_replace($illegal, $legal, $str); //replace them return $str; } /** * Authenticate user against LDAP server. * * @param string $login * @param string $password * @return mixed */ function authenticate($login, $password) { // Special handling for admin@localhost - authenticate against db, not ldap. // It is a fallback mechanism when admin account in LDAP directory does not exist or is misconfigured. if ($login == 'admin@localhost') { import('auth.Auth_db'); return Auth_db::authenticate($login, $password); } if (!function_exists('ldap_bind')) { die ('php_ldap extension not loaded!'); } if (empty($this->params['server']) || empty($this->params['base_dn'])) { die('You must set server and base_dn in AUTH_MODULE_PARAMS in config.php'); } $member_of = @$this->params['member_of']; $lc = ldap_connect($this->params['server']); if (isTrue('DEBUG')) { echo '
'; echo '$lc='; var_dump($lc); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if (!$lc) return false; ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($lc, LDAP_OPT_REFERRALS, 0); if (isTrue('DEBUG')) { ldap_set_option($lc, LDAP_OPT_DEBUG_LEVEL, 7); } // We need to handle Windows AD and OpenLDAP differently. if ($this->params['type'] == 'ad') { // Check if user specified full login. if (strpos($login, '@') === false) { // Append default domain. $login .= '@' . $this->params['default_domain']; } if (isTrue('DEBUG')) { echo '$login='; var_dump($login); echo '
'; } $lb = @ldap_bind($lc, $login, $password); if (isTrue('DEBUG')) { echo '$lb='; var_dump($lb); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if (!$lb) { ldap_unbind($lc); return false; } if ($member_of) { // Get groups the user is a member of from AD LDAP server. $filter = 'userPrincipalName='.Auth_ldap::ldap_escape($login); $fields = array('memberof'); $sr = @ldap_search($lc, $this->params['base_dn'], $filter, $fields); if (isTrue('DEBUG')) { echo '$sr='; var_dump($sr); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if (!$sr) { ldap_unbind($lc); return false; } $entries = @ldap_get_entries($lc, $sr); if (isTrue('DEBUG')) { echo '$entries='; var_dump($entries); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if ($entries === false) { ldap_unbind($lc); return false; } $groups = array(); // Extract group names. Assume the groups are in format: CN=,... for ($i = 0; $i < @$entries[0]['memberof']['count']; $i++) { $grp = $entries[0]['memberof'][$i]; $grp_fields = explode(',', $grp); $groups[] = substr($grp_fields[0], 3); } if (isTrue('DEBUG')) { echo '$member_of'; var_dump($member_of); echo '
'; }; // Check for group membership. foreach ($member_of as $check_grp) { if (!in_array($check_grp, $groups)) { ldap_unbind($lc); return false; } } } ldap_unbind($lc); return array('login' => $login, 'data' => $entries, 'member_of' => $groups); } if ($this->params['type'] == 'openldap') { // Assuming OpenLDAP server. $login_oldap = 'uid='.$login.','.$this->params['base_dn']; if (isTrue('DEBUG')) { echo '$login_oldap='; var_dump($login_oldap); echo '
'; } // check if the user specified full login if (strpos($login, '@') === false) { // append default domain $login .= '@' . $this->params['default_domain']; } $lb = @ldap_bind($lc, $login_oldap, $password); if (isTrue('DEBUG')) { echo '$lb='; var_dump($lb); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if (!$lb) { ldap_unbind($lc); return false; } if ($member_of) { // TODO: Fix this for OpenLDAP, as samaccountname has nothing to do with it. // get groups $filter = 'samaccountname='.Auth_ldap::ldap_escape($login_oldap); $fields = array('samaccountname', 'mail', 'memberof', 'department', 'displayname', 'telephonenumber', 'primarygroupid'); $sr = @ldap_search($lc, $this->params['base_dn'], $filter, $fields); if (isTrue('DEBUG')) { echo '$sr='; var_dump($sr); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } // if search failed it's likely that account is disabled if (!$sr) { ldap_unbind($lc); return false; } $entries = @ldap_get_entries($lc, $sr); if (isTrue('DEBUG')) { echo '$entries='; var_dump($entries); echo '
'; echo 'ldap_error()='; echo ldap_error($lc); echo '
'; } if ($entries === false) { ldap_unbind($lc); return false; } $groups = array(); // extract group names from // assuming the groups are in format: CN=,... for ($i = 0; $i < @$entries[0]['memberof']['count']; $i++) { $grp = $entries[0]['memberof'][$i]; $grp_fields = explode(',', $grp); $groups[] = substr($grp_fields[0], 3); } if (isTrue('DEBUG')) { echo '$member_of'; var_dump($member_of); echo '
'; } // check for group membership foreach ($member_of as $check_grp) { if (!in_array($check_grp, $groups)) { ldap_unbind($lc); return false; } } } ldap_unbind($lc); return array('login' => $login, 'data' => $entries, 'member_of' => $groups); } // Server type is neither 'ad' or 'openldap'. return false; } function isPasswordExternal() { return true; } }