/** * Perform an LDAP search. * @param string $basedn * The search base. If NULL, we use $this->basedn. should not be esacaped * * @param string $filter * The search filter. such as sAMAccountName=jbarclay. attribute values (e.g. jbarclay) should be esacaped before calling * @param array $attributes * List of desired attributes. If omitted, we only return "dn". * * @remaining params mimick ldap_search() function params * * @return * An array of matching entries->attributes, or FALSE if the search is empty. */ function search($base_dn = NULL, $filter, $attributes = array(), $attrsonly = 0, $sizelimit = 0, $timelimit = 0, $deref = LDAP_DEREF_NEVER, $scope = LDAP_SCOPE_SUBTREE) { //debug("feaux_server_filter=$filter"); $filter = trim(str_replace(array("\n", " "), array('', ''), $filter)); // for test matching simplicity remove line breaks and tab spacing // debug("filter=$filter"); // debug('search'); debug("base_dn: $base_dn"); debug("filter:<pre>$filter</pre>"); $my_debug = $filter == "(samaccountname=verykool)" || $filter == "(sAMAccountName=verykool)"; // if ($my_debug) {debug('attributes'); debug($attributes); debug('search'); debug("base_dn: $base_dn"); debug("filter:<pre>$filter</pre>");} if ($base_dn == NULL) { if (count($this->basedn) == 1) { $base_dn = $this->basedn[0]; } else { return FALSE; } } // return prepolulated search results in test data array if present if (isset($this->searchResults[$filter][$base_dn])) { // if ($my_debug) {debug('set search results'); debug($this->searchResults[$filter][$base_dn]);} return $this->searchResults[$filter][$base_dn]; } $base_dn = drupal_strtolower($base_dn); $filter = strtolower(trim($filter, "()")); list($filter_attribute, $filter_value) = explode('=', $filter); if (strtolower($filter_attribute) == 'dn') { // don't allow filtering on dn. filters should use distinguishedName continue; } // $filter_value = ldap_pear_unescape_filter_value($filter_value); // need to perform feaux ldap search here with data in $results = array(); // debug('test users'); debug($this->testUsers); debug("filter_attribute=$filter_attribute, filter_value=$filter_value"); foreach ($this->testUsers as $dn => $user_data) { $my_debug2 = $my_debug && $dn == 'cn=Flintstone\\, Wilma,ou=guest accounts,dc=ad,dc=myuniversity,dc=edu'; // if ($my_debug2) {debug('test user ' . $dn); debug($user_data);} $user_data_lcase = array(); foreach ($user_data['attr'] as $attr => $values) { $user_data_lcase['attr'][drupal_strtolower($attr)] = $values; } // if not in basedn, skip // eg. basedn ou=campus accounts,dc=ad,dc=myuniversity,dc=edu // should be leftmost string in: // cn=jdoe,ou=campus accounts,dc=ad,dc=myuniversity,dc=edu $dn = strtolower($dn); $pos = stripos($dn, $base_dn); if ($pos === FALSE || strcasecmp($base_dn, substr($dn, 0, $pos + 1)) == FALSE) { if ($my_debug2) { debug("dn={$dn} not in base_dn={$base_dn}"); } continue; // not in basedn } // check for mixed case and lowercase attribute's existance // trying to mimic ldap implementation if (isset($user_data['attr'][$filter_attribute])) { $contained_values = $user_data['attr'][$filter_attribute]; // if ($my_debug2) { debug('mixed case match success');} } elseif (isset($user_data_lcase['attr'][ldap_server_massage_text($filter_attribute, 'attr_name', LDAP_SERVER_MASSAGE_QUERY_ARRAY)])) { $contained_values = $user_data_lcase['attr'][ldap_server_massage_text($filter_attribute, 'attr_name', LDAP_SERVER_MASSAGE_QUERY_ARRAY)]; // if ($my_debug2) { debug('lower case match success');} } else { // if ($my_debug2) {debug('match fail');} continue; } //debug("contained_values"); debug($contained_values); unset($contained_values['count']); if (!in_array($filter_value, array_values($contained_values))) { // if ($my_debug2) { debug("match to value $filter_attribute=$filter_value failed");} continue; } // loop through all attributes, if any don't match continue $user_data_lcase['attr']['distinguishedname'] = $dn; if ($attributes) { $selected_user_data = array(); foreach ($attributes as $i => $attr_name) { $key = drupal_strtolower($attr_name); $selected_user_data[$key] = isset($user_data_lcase['attr'][$key]) ? $user_data_lcase['attr'][$key] : NULL; } $results[] = $selected_user_data; } elseif (isset($user_data_lcase['attr'])) { $results[] = $user_data_lcase['attr']; } } // if ($my_debug) { debug("results post user loop"); debug($results);} foreach ($this->testGroups as $dn => $group_data) { // debug("group dn $dn"); debug($group_data); // if not in basedn, skip // eg. basedn ou=campus accounts,dc=ad,dc=myuniversity,dc=edu // should be leftmost string in: // cn=jdoe,ou=campus accounts,dc=ad,dc=myuniversity,dc=edu $pos = strpos($dn, $base_dn); if ($pos === FALSE || strcasecmp($base_dn, substr($dn, 0, $pos + 1)) == FALSE) { continue; // not in basedn } else { } // if doesn't filter attribute has no data, continue if (!isset($group_data['attr'][$filter_attribute])) { continue; } // if doesn't match filter, continue $contained_values = $group_data['attr'][$filter_attribute]; unset($contained_values['count']); if (!in_array($filter_value, array_values($contained_values))) { continue; } // loop through all attributes, if any don't match continue // $group_data['attr']['distinguishedname'] = $dn; // $group_data['distinguishedname'] = $dn; if ($attributes) { $selected_group_data = array(); foreach ($attributes as $key => $value) { $selected_group_data[$key] = isset($group_data['attr'][$key]) ? $group_data['attr'][$key] : NULL; } $results[] = $selected_group_data; } else { $results[] = $group_data['attr']; } // debug($results); } $results['count'] = count($results); $results = $results['count'] > 0 ? $results : FALSE; // if ($my_debug) { debug('search-results 2'); debug($results);} return $results; }
/** * Queries LDAP server for the user. * * @param string $drupal_user_name * * @param string or int $prov_event * This could be anything, particularly when used by other modules. Other modules should use string like 'mymodule_myevent' * LDAP_USER_EVENT_ALL signifies get all attributes needed by all other contexts/ops * * @return associative array representing ldap data of a user. for example of returned value. * 'sid' => ldap server id * 'mail' => derived from ldap mail (not always populated). * 'dn' => dn of user * 'attr' => single ldap entry array in form returned from ldap_search() extension, e.g. * 'dn' => dn of entry */ function userUserNameToExistingLdapEntry($drupal_user_name, $ldap_context = NULL) { $watchdog_tokens = array('%drupal_user_name' => $drupal_user_name); $ldap_username = $this->userUsernameToLdapNameTransform($drupal_user_name, $watchdog_tokens); if (!$ldap_username) { return FALSE; } if (!$ldap_context) { $attributes = array(); } else { $attribute_maps = ldap_servers_attributes_needed($this->sid, $ldap_context); $attributes = array_keys($attribute_maps); } foreach ($this->basedn as $basedn) { if (empty($basedn)) { continue; } $filter = '(' . $this->user_attr . '=' . ldap_server_massage_text($ldap_username, 'attr_value', LDAP_SERVER_MASSAGE_QUERY_LDAP) . ')'; $result = $this->search($basedn, $filter, $attributes); if (!$result || !isset($result['count']) || !$result['count']) { continue; } // Must find exactly one user for authentication to work. if ($result['count'] != 1) { $count = $result['count']; watchdog('ldap_servers', "Error: !count users found with {$filter} under {$basedn}.", array('!count' => $count), WATCHDOG_ERROR); continue; } $match = $result[0]; // These lines serve to fix the attribute name in case a // naughty server (i.e.: MS Active Directory) is messing the // characters' case. // This was contributed by Dan "Gribnif" Wilga, and described // here: http://drupal.org/node/87833 $name_attr = $this->user_attr; if (isset($match[$name_attr][0])) { // leave name } elseif (isset($match[drupal_strtolower($name_attr)][0])) { $name_attr = drupal_strtolower($name_attr); } else { if ($this->bind_method == LDAP_SERVERS_BIND_METHOD_ANON_USER) { $result = array('dn' => $match['dn'], 'mail' => $this->userEmailFromLdapEntry($match), 'attr' => $match, 'sid' => $this->sid); return $result; } else { continue; } } // Finally, we must filter out results with spaces added before // or after, which are considered OK by LDAP but are no good for us // We allow lettercase independence, as requested by Marc Galera // on http://drupal.org/node/97728 // // Some setups have multiple $name_attr per entry, as pointed out by // Clarence "sparr" Risher on http://drupal.org/node/102008, so we // loop through all possible options. foreach ($match[$name_attr] as $value) { if (drupal_strtolower(trim($value)) == drupal_strtolower($ldap_username)) { $result = array('dn' => $match['dn'], 'mail' => $this->userEmailFromLdapEntry($match), 'attr' => $match, 'sid' => $this->sid); return $result; } } } }
public function deriveEmailFromEntry($ldap_entry) { if ($this->mail_attr) { // not using template return @$ldap_entry[ldap_server_massage_text($this->mail_attr, 'attr_name', LDAP_SERVER_MASSAGE_QUERY_ARRAY)][0]; } elseif ($this->mail_template) { // template is of form [cn]@illinois.edu ldap_server_module_load_include('inc', 'ldap_servers', 'ldap_servers.functions'); return ldap_server_token_replace($ldap_entry, $this->mail_template); } else { return FALSE; } }