/** * Get password expiration time for a given user from Active Directory * * @param string $pwdlastset The time last time we changed the password. * @param resource $lcapconn The open LDAP connection. * @param string $user_dn The distinguished name of the user we are checking. * * @return string $unixtime */ function ldap_get_ad_pwdexpire($pwdlastset, $ldapconn, $user_dn) { global $CFG; if (!function_exists('bcsub')) { error_log($this->errorlogtag . get_string('needbcmath', 'auth_ldap')); return 0; } // If UF_DONT_EXPIRE_PASSWD flag is set in user's // userAccountControl attribute, the password doesn't expire. $sr = ldap_read($ldapconn, $user_dn, '(objectClass=*)', array('userAccountControl')); if (!$sr) { error_log($this->errorlogtag . get_string('useracctctrlerror', 'auth_ldap', $user_dn)); // Don't expire password, as we are not sure if it has to be // expired or not. return 0; } $entry = ldap_get_entries_moodle($ldapconn, $sr); $info = array_change_key_case($entry[0], CASE_LOWER); $useraccountcontrol = $info['useraccountcontrol'][0]; if ($useraccountcontrol & UF_DONT_EXPIRE_PASSWD) { // Password doesn't expire. return 0; } // If pwdLastSet is zero, the user must change his/her password now // (unless UF_DONT_EXPIRE_PASSWD flag is set, but we already // tested this above) if ($pwdlastset === '0') { // Password has expired return -1; } // ---------------------------------------------------------------- // Password expiration time in Active Directory is the composition of // two values: // // - User's pwdLastSet attribute, that stores the last time // the password was changed. // // - Domain's maxPwdAge attribute, that sets how long // passwords last in this domain. // // We already have the first value (passed in as a parameter). We // need to get the second one. As we don't know the domain DN, we // have to query rootDSE's defaultNamingContext attribute to get // it. Then we have to query that DN's maxPwdAge attribute to get // the real value. // // Once we have both values, we just need to combine them. But MS // chose to use a different base and unit for time measurements. // So we need to convert the values to Unix timestamps (see // details below). // ---------------------------------------------------------------- $sr = ldap_read($ldapconn, ROOTDSE, '(objectClass=*)', array('defaultNamingContext')); if (!$sr) { error_log($this->errorlogtag . get_string('rootdseerror', 'auth_ldap')); return 0; } $entry = ldap_get_entries_moodle($ldapconn, $sr); $info = array_change_key_case($entry[0], CASE_LOWER); $domaindn = $info['defaultnamingcontext'][0]; $sr = ldap_read($ldapconn, $domaindn, '(objectClass=*)', array('maxPwdAge')); $entry = ldap_get_entries_moodle($ldapconn, $sr); $info = array_change_key_case($entry[0], CASE_LOWER); $maxpwdage = $info['maxpwdage'][0]; // ---------------------------------------------------------------- // MSDN says that "pwdLastSet contains the number of 100 nanosecond // intervals since January 1, 1601 (UTC), stored in a 64 bit integer". // // According to Perl's Date::Manip, the number of seconds between // this date and Unix epoch is 11644473600. So we have to // substract this value to calculate a Unix time, once we have // scaled pwdLastSet to seconds. This is the script used to // calculate the value shown above: // // #!/usr/bin/perl -w // // use Date::Manip; // // $date1 = ParseDate ("160101010000 UTC"); // $date2 = ParseDate ("197001010000 UTC"); // $delta = DateCalc($date1, $date2, \$err); // $secs = Delta_Format($delta, 0, "%st"); // print "$secs \n"; // // MSDN also says that "maxPwdAge is stored as a large integer that // represents the number of 100 nanosecond intervals from the time // the password was set before the password expires." We also need // to scale this to seconds. Bear in mind that this value is stored // as a _negative_ quantity (at least in my AD domain). // // As a last remark, if the low 32 bits of maxPwdAge are equal to 0, // the maximum password age in the domain is set to 0, which means // passwords do not expire (see // http://msdn2.microsoft.com/en-us/library/ms974598.aspx) // // As the quantities involved are too big for PHP integers, we // need to use BCMath functions to work with arbitrary precision // numbers. // ---------------------------------------------------------------- // If the low order 32 bits are 0, then passwords do not expire in // the domain. Just do '$maxpwdage mod 2^32' and check the result // (2^32 = 4294967296) if (bcmod($maxpwdage, 4294967296) === '0') { return 0; } // Add up pwdLastSet and maxPwdAge to get password expiration // time, in MS time units. Remember maxPwdAge is stored as a // _negative_ quantity, so we need to substract it in fact. $pwdexpire = bcsub($pwdlastset, $maxpwdage); // Scale the result to convert it to Unix time units and return // that value. return bcsub(bcdiv($pwdexpire, '10000000'), '11644473600'); }
/** * Checks if user belongs to specific group(s) or is in a subtree. * * Returns true if user belongs to a group in grupdns string OR if the * DN of the user is in a subtree of the DN provided as "group" * * @param mixed $ldapconnection A valid LDAP connection. * @param string $userid LDAP user id (dn/cn/uid/...) to test membership for. * @param array $group_dns arrary of group dn * @param string $member_attrib the name of the membership attribute. * @return boolean * */ function ldap_isgroupmember($ldapconnection, $userid, $group_dns, $member_attrib) { if (empty($ldapconnection) || empty($userid) || empty($group_dns) || empty($member_attrib)) { return false; } $result = false; foreach ($group_dns as $group) { $group = trim($group); if (empty($group)) { continue; } // Check cheaply if the user's DN sits in a subtree of the // "group" DN provided. Granted, this isn't a proper LDAP // group, but it's a popular usage. if (stripos(strrev(strtolower($userid)), strrev(strtolower($group))) === 0) { $result = true; break; } $search = ldap_read($ldapconnection, $group, '(' . $member_attrib . '=' . ldap_filter_addslashes($userid) . ')', array($member_attrib)); if (!empty($search) && ldap_count_entries($ldapconnection, $search)) { $info = ldap_get_entries_moodle($ldapconnection, $search); if (count($info) > 0) { // User is member of group $result = true; break; } } } return $result; }
/** * Reads user information from ldap and returns it in array() * * Function should return all information available. If you are saving * this information to moodle user-table you should honor syncronization flags * * @param object $ldapauth the ldap authentication instance * @param string $username username * @param array $options an array with CLI input options * * @return mixed array with no magic quotes or false on error */ function local_ent_installer_get_userinfo($ldapauth, $username, $options = array()) { static $entattributes; // Load some cached static data. if (!isset($entattributes)) { // aggregate additional ent specific attributes that hold interesting information $configattribs = get_config('local_ent_installer', 'ent_userinfo_attributes'); if (empty($configattribs)) { $entattributes = array('ENTPersonFonctions', 'ENTPersonJointure', 'ENTEleveClasses', 'ENTEleveGroupes', 'ENTEleveTransport', 'ENTEleveRegime', 'ENTPersonProfils', 'objectGUID'); } else { $entattributes = explode(',', $configattribs); } } $extusername = core_text::convert($username, 'utf-8', $ldapauth->config->ldapencoding); $ldapconnection = $ldapauth->ldap_connect(); if (!($user_dn = $ldapauth->ldap_find_userdn($ldapconnection, $extusername))) { $ldapauth->ldap_close(); return false; } $search_attribs = array(); $attrmap = $ldapauth->ldap_attributes(); foreach ($attrmap as $key => $values) { if (!is_array($values)) { $values = array($values); } foreach ($values as $value) { if (!in_array($value, $search_attribs)) { array_push($search_attribs, $value); } } } foreach ($entattributes as $value) { if (!in_array($value, $search_attribs)) { array_push($search_attribs, $value); // Add attributes to $attrmap so they are pulled down into final user object. $attrmap[$value] = strtolower($value); } } if ($options['verbose']) { mtrace("Getting {$user_dn} for " . implode(',', $search_attribs)); } if (!($user_info_result = ldap_read($ldapconnection, $user_dn, '(objectClass=*)', $search_attribs))) { $ldapauth->ldap_close(); return false; } $user_entry = ldap_get_entries_moodle($ldapconnection, $user_info_result); if (empty($user_entry)) { $ldapauth->ldap_close(); return false; // Entry not found. } $result = array(); foreach ($attrmap as $key => $values) { if (!is_array($values)) { $values = array($values); } $ldapval = NULL; foreach ($values as $value) { $entry = array_change_key_case($user_entry[0], CASE_LOWER); if ($value == 'dn' || $value == 'distinguishedname') { $result[$key] = $user_dn; continue; } if (!array_key_exists($value, $entry)) { if ($options['verbose']) { mtrace("Requested value {$value} but missing in record"); } continue; // wrong data mapping! } if ($value == 'objectguid') { if (strlen($entry[$value][0]) == 16) { $tmp = bin2hex($entry[$value][0]); $t = $tmp[6] . $tmp[7] . $tmp[4] . $tmp[5] . $tmp[2] . $tmp[3] . $tmp[0] . $tmp[1] . '-'; $t .= $tmp[10] . $tmp[11] . $tmp[8] . $tmp[9] . '-'; $t .= $tmp[14] . $tmp[15] . $tmp[12] . $tmp[13] . '-'; $t .= substr($tmp, 16, 4) . '-'; $t .= substr($tmp, 20); $objectguid = $t; } $newval = $objectguid; } else { if ($value == 'entelevegroupes' && is_array($entry[$value])) { $newval = array(); foreach ($entry[$value] as $subkey => $subvalue) { if ($subkey !== 'count') { $newval[] = core_text::convert($subvalue, $ldapauth->config->ldapencoding, 'utf-8'); } } } else { if (is_array($entry[$value])) { $newval = core_text::convert($entry[$value][0], $ldapauth->config->ldapencoding, 'utf-8'); } else { $newval = core_text::convert($entry[$value], $ldapauth->config->ldapencoding, 'utf-8'); } } } if (!empty($newval)) { // Favour ldap entries that are set. $ldapval = $newval; } } if (!is_null($ldapval)) { $result[$key] = $ldapval; } } $ldapauth->ldap_close(); return $result; }
/** * * returns the distinct values of the target LDAP attribute * these will be the idnumbers of the synched Moodle cohorts * @returns array of string */ function get_attribute_distinct_values() { //return array ('affiliate','retired','student','faculty','staff','employee','affiliate','member','alum','emeritus','researcher'); global $CFG, $DB; // only these cohorts will be synched if (!empty($this->config->cohort_synching_ldap_attribute_idnumbers)) { return explode(',', $this->config->cohort_synching_ldap_attribute_idnumbers); } //build a filter to fetch all users having something in the target LDAP attribute $filter = '(&(' . $this->config->user_attribute . '=*)' . $this->config->objectclass . ')'; $filter = '(&' . $filter . '(' . $this->config->cohort_synching_ldap_attribute_attribute . '=*))'; if ($CFG->debug_ldap_groupes) { pp_print_object('looking for ', $filter); } $ldapconnection = $this->ldap_connect(); $contexts = explode(';', $this->config->contexts); if (!empty($this->config->create_context)) { array_push($contexts, $this->config->create_context); } $matchings = array(); foreach ($contexts as $context) { $context = trim($context); if (empty($context)) { continue; } if ($this->config->search_sub) { // Use ldap_search to find first user from subtree $ldap_result = ldap_search($ldapconnection, $context, $filter, array($this->config->cohort_synching_ldap_attribute_attribute)); } else { // Search only in this context $ldap_result = ldap_list($ldapconnection, $context, $filter, array($this->config->cohort_synching_ldap_attribute_attribute)); } if (!$ldap_result) { continue; } // this API function returns all attributes as an array // wether they are single or multiple $users = ldap_get_entries_moodle($ldapconnection, $ldap_result); // Add found DISTINCT values to list for ($i = 0; $i < count($users); $i++) { $count = $users[$i][$this->config->cohort_synching_ldap_attribute_attribute]['count']; for ($j = 0; $j < $count; $j++) { $value = textlib::convert($users[$i][$this->config->cohort_synching_ldap_attribute_attribute][$j], $this->config->ldapencoding, 'utf-8'); if (!in_array($value, $matchings)) { array_push($matchings, $value); } } } } $this->ldap_close(); return $matchings; }
/** * Get ldap entry by user name * @param required $username */ private function _getldap_entry($username) { $ap_ldap = new auth_plugin_ldap(); $ldapconnection = $ap_ldap->ldap_connect(); $user_dn = $ap_ldap->ldap_find_userdn($ldapconnection, $username); $sr = ldap_read($ldapconnection, $user_dn, 'objectclass=*', array()); if ($sr) { $info = ldap_get_entries_moodle($ldapconnection, $sr); } else { return false; } return $info; }