ldap_servers.functions.inc in Lightweight Directory Access Protocol (LDAP) 7
Same filename and directory in other branches
collection of functions that don't belong in server object
File
ldap_servers/ldap_servers.functions.incView source
<?php
// $Id: ldap_servers.functions.inc,v 1.3.2.1 2011/02/08 06:01:00 johnbarclay Exp $
/**
* @file
* collection of functions that don't belong in server object
*/
function ldap_server_module_load_include($type, $module, $name = NULL) {
$result = module_load_include($type, $module, $name);
if ($result === FALSE) {
print "Failed to load file {$name}.{$type} in module {$module}";
drupal_exit();
}
}
/**
* returns new user account if created, otherwise integer error message such
* as LDAP_CREATE_ACCOUNT_ALREADY_EXISTS, LDAP_CREATE_ERROR
*
*/
// $account = ldap_create_drupal_account($authname, $accountname, $ldap_user['mail'], $ldap_user['dn'], $sid, array(), $status);
function ldap_create_drupal_account($authname, $accountname, $mail, $dn, $sid, $status, $edit) {
$edit['name'] = $accountname;
$edit['pass'] = user_password(20);
$edit['mail'] = $mail;
$edit['init'] = $mail;
$edit['status'] = $status;
if (!isset($edit['signature'])) {
$edit['signature'] = '';
}
// save 'init' data to know the origin of the ldap authentication provisioned account
$edit['data']['ldap_authentication']['init'] = array(
'sid' => $sid,
'dn' => $dn,
'mail' => $mail,
);
if (!($account = user_save(NULL, $edit))) {
drupal_set_message(t('User account creation failed because of system problems.'), 'error');
return FALSE;
}
else {
user_set_authmaps($account, array(
'authname_ldap_authentication' => $authname,
));
}
return $account;
}
/**
* Modify an LDAP Entry
*/
function ldap_user_modify($userdn, $attributes, $ldap_server) {
$status = ldap_modify($ldap_server->connection, $userdn, $attributes);
if (!$status) {
watchdog('ldap_servers', 'Error: user_modify() failed to modify ldap entry w/ base DN "!dn" with values: !values', array(
'!dn' => $userdn,
'!value' => var_export($attributes, TRUE),
), WATCHDOG_ERROR);
}
return $status;
}
/**
* Modify a password
*/
function ldap_password_modify($userdn, $new_password, $ldap_server) {
$new_password = "\"" . $new_password . "\"";
$len = drupal_strlen($new_password);
$new_pass = NULL;
for ($i = 0; $i < $len; $i++) {
$new_pass .= "{$new_password[$i]}\0";
}
$status = ldap_mod_replace($ldap_server->connection, $userdn, array(
'unicodePwd' => $new_pass,
));
if (!$status) {
watchdog('ldap_servers', 'Error: password_modify() failed to modify ldap password w/ base DN "!dn"', array(
'!dn' => $userdn,
), WATCHDOG_ERROR);
}
return $status;
}
/**
*
* this attempts to find bad dns, but should only be used as warningswe
* as the ldap spec allows for any old character to be escaped and ldap
* implementations may not follow the spec.
*
* http://www.ietf.org/rfc/rfc2253.txt
*
*/
function ldap_baddn($dn, $dn_name) {
$result = array();
$valid_attr_name = '[_a-zA-Z\\d\\s]';
$valid_attr_values = '[_\\-a-zA-Z\\d\\s]';
$regex = '/^(' . $valid_attr_name . '*\\=' . $valid_attr_values . '*[,]{1})*(' . $valid_attr_name . '*\\=' . $valid_attr_values . '*){1}$/';
$match = preg_match($regex, $dn) ? TRUE : FALSE;
$result['boolean'] = $match;
if (!$match) {
$tokens = array(
'%dn' => htmlspecialchars($dn),
'%dn_name' => $dn_name,
);
$result['text'] = t('Possible invalid format for:', $tokens) . '<em>' . $tokens['%dn'] . '</em>.<br/> ' . t('The format may be correct for your ldap, but please double check.', $tokens);
}
return $result;
}
/**
*
* this attempts to find bad dns, but should only be used as warningswe
* as the ldap spec allows for any old character to be escaped and ldap
* implementations may not follow the spec.
*
* http://www.ietf.org/rfc/rfc2253.txt
*
*/
function ldap_badattr($attr, $attr_name) {
$result = array();
$valid_attr_name = '[_a-zA-Z\\d\\s]';
$regex = '/^(' . $valid_attr_name . '){1,}$/';
$match = preg_match($regex, $attr) ? TRUE : FALSE;
$result['boolean'] = $match;
if (!$match) {
$tokens = array(
'%attr' => htmlspecialchars($attr),
'%attr_name' => $attr_name,
);
$result['text'] = t('Possible invalid format for %attr_name:', $tokens) . ' <code><em>' . $tokens['%attr'] . '</em></code><br/>' . t('The format may be correct for your ldap, but please double check.', $tokens);
}
return $result;
}
/**
* @param array $ldap_entry
* @param string $text
* @return string text with tokens replaced
*/
function ldap_server_token_replace($ldap_entry, $text) {
$desired_tokens = ldap_server_tokens_needed_for_template($text);
$tokens = ldap_server_tokenize_entry($ldap_entry, $desired_tokens, LDAP_SERVERS_TOKEN_PRE, LDAP_SERVERS_TOKEN_POST);
$result = str_replace(array_keys($tokens), array_values($tokens), $text);
return $result;
}
/**
* Turn an ldap entry into a token array suitable for the t() function
* @param ldap entry array $ldap_entry
* @param string prefix token prefix such as !,%,[
* @param string suffix token suffix such as ]
* @param $token_keys either an array of key names such as array('cn', 'dn') or string 'all' to return all tokens.
* @return token array suitable for t() functions of with lowercase keys as exemplified below
an ldap entry such as:
'dn' => 'cn=jdoe,ou=campus accounts,ou=toledo campus,dc=ad,dc=myuniversity,dc=edu',
'mail' => array( 0 => 'jdoe@myuniversity.edu', 'count' => 1),
'sAMAccountName' => array( 0 => 'jdoe', 'count' => 1),
should return tokens such as:
-- from dn attribute
[cn] = jdoe
[cn:0] = jdoe
[cn:last] => jdoe
[ou] = campus accounts
[ou:0] = campus accounts
[ou:1] = toledo campus
[ou:last] = toledo campus
[dc] = ad
[dc:0] = ad
[dc:1] = myuniversity
[dc:2] = edu
[dc:last] = edu
-- from other attributes
[mail] = jdoe@myuniversity.edu
[mail:0] = jdoe@myuniversity.edu
[mail:last] = jdoe@myuniversity.edu
[samaccountname] = jdoe
[samaccountname:0] = jdoe
[samaccountname:last] = jdoe
*/
function ldap_server_tokenize_entry($ldap_entry, $token_keys = 'all', $pre = LDAP_SERVERS_TOKEN_PRE, $post = LDAP_SERVERS_TOKEN_POST) {
$detailed_watchdog_log = variable_get('ldap_help_watchdog_detail', 0);
$tokens = array();
// 1. tokenize dn
$dn_parts = ldap_explode_dn($ldap_entry['dn'], 0);
// escapes attribute values, need to be unescaped later.
unset($dn_parts['count']);
$parts_count = array();
$parts_last_value = array();
foreach ($dn_parts as $pair) {
list($attr_name, $attr_value) = explode('=', $pair);
$attr_value = ldap_pear_unescape_dn_value($attr_value);
if (!($attr_value = ldap_server_check_plain($attr_value, $attr_name))) {
continue;
}
if (!isset($parts_count[$attr_name])) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . $post] = $attr_value;
$parts_count[$attr_name] = 0;
}
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . (int) $parts_count[$attr_name] . $post] = $attr_value;
$parts_last_value[$attr_name] = $attr_value;
$parts_count[$attr_name]++;
}
foreach ($parts_count as $attr_name => $count) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . 'last' . $post] = $parts_last_value[$attr_name];
}
// tokenize other attributes
if ($token_keys == 'all') {
$token_keys = array_keys($ldap_entry);
$token_keys = array_filter($token_keys, "is_string");
foreach ($token_keys as $attr_name) {
$attr_value = $ldap_entry[$attr_name];
if (is_array($attr_value) && is_scalar($attr_value[0]) && $attr_value['count'] == 1) {
if ($value = ldap_server_check_plain($attr_value[0], $attr_name)) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . $post] = $value;
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . '0' . $post] = $value;
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . 'last' . $post] = $value;
}
}
elseif (is_array($attr_value) && $attr_value['count'] > 1) {
if ($value = ldap_server_check_plain($attr_value[$attr_value['count'] - 1], $attr_name)) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . 'last' . $post] = $value;
}
for ($i = 0; $i < $attr_value['count']; $i++) {
if ($value = ldap_server_check_plain($attr_value[$i], $attr_name)) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . LDAP_SERVERS_TOKEN_DEL . $i . $post] = $value;
}
}
}
elseif (is_scalar($attr_value)) {
if ($value = ldap_server_check_plain($attr_value, $attr_name)) {
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . $post] = $value;
}
}
}
}
else {
foreach ($token_keys as $token_key) {
$parts = explode(LDAP_SERVERS_TOKEN_DEL, $token_key);
$last_key = $parts[count($parts) - 1];
if ($last_key == 'last') {
$attr_name = join(LDAP_SERVERS_TOKEN_DEL, array_pop($parts));
$count = $ldap_entry[$attr_name]['count'];
$value = $ldap_entry[$attr_name][$count - 1];
}
elseif (is_numeric($last_key) || $last_key == '0') {
$discard = array_pop($parts);
$attr_name = join(LDAP_SERVERS_TOKEN_DEL, $parts);
$value = $ldap_entry[$attr_name][(int) $last_key];
}
elseif (empty($ldap_entry[$token_key])) {
continue;
}
else {
$attr_name = $token_key;
$value = $ldap_entry[$token_key][0];
}
$value = ldap_server_check_plain($value, $token_key);
if ($value === FALSE) {
continue;
// don't tokenize data that can't pass check_plain
}
$tokens[$pre . ldap_server_massage_text($attr_name, 'attr_name', LDAP_SERVER_MASSAGE_TOKEN_REPLACE) . $post] = $value;
}
}
// include the dn. it will not be handled correctly by previous loops
$tokens[$pre . 'dn' . $post] = ldap_server_check_plain($ldap_entry['dn']);
return $tokens;
}
function ldap_server_check_plain($text, $attr_name = 'attribute', $detailed_watchdog_log = FALSE) {
try {
$text = @check_plain($text);
} catch (Exception $e) {
if ($detailed_watchdog_log) {
$watchdog_tokens = array(
'%attr_name' => $attr_name,
);
watchdog('ldap_servers', 'skipped tokenization of attribute %attr_name because the value would not pass check_plain function.', $watchdog_tokens, WATCHDOG_DEBUG);
}
$text = FALSE;
}
return $text;
}
/**
* @param string $template in form [cn]@myuniversity.edu
* @return array of all tokens in the template such as array('cn')
*/
function ldap_server_tokens_needed_for_template($template, $pre = LDAP_SERVERS_TOKEN_PRE, $post = LDAP_SERVERS_TOKEN_POST) {
preg_match_all('/
\\[ # [ - pattern start
([^\\[\\]]*) # match $type not containing whitespace : [ or ]
\\] # ] - pattern end
/x', $template, $matches);
return @$matches[1];
}
function ldap_servers_show_sample_user_tokens($sid) {
$ldap_server = ldap_servers_get_servers($sid, 'all', TRUE);
$test_username = $ldap_server->testingDrupalUsername;
if (!$test_username || !($ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_SERVICE_ACCT || $ldap_server->bind_method == LDAP_SERVERS_BIND_METHOD_ANON)) {
return FALSE;
}
if ($ldap_user = $ldap_server
->user_lookup($test_username)) {
$table = theme('ldap_server_ldap_entry_table', array(
'entry' => $ldap_user['attr'],
'username' => $test_username,
));
}
else {
$table = '<p>' . t('No sample user data found') . '</p>';
}
return $table;
}
/**
* function to massage (change case, escape, unescape) ldap attribute names
* and values. The primary purpose of this is to articulate and ensure consistency
* across ldap modules.
*
* @param mixed $value to be massaged
* @param enum $value_type = 'attr_name' or 'attr_value;
* @param enum $context...see LDAP_SERVER_MASSAGE_* constants
*
* .e.g. ldap_server_massage_text($value, 'attr_value', LDAP_SERVER_MASSAGE_QUERY_LDAP)
* ldap_server_massage_text($value, 'attr_value', LDAP_SERVER_MASSAGE_QUERY_ARRAY)
*/
function ldap_server_massage_text($value, $value_type, $context) {
$scalar = is_scalar($value);
if ($value_type == 'attr_value') {
if ($context == LDAP_SERVER_MASSAGE_QUERY_LDAP) {
$value = ldap_pear_escape_filter_value($value);
}
elseif ($context == LDAP_SERVER_MASSAGE_STORE_LDAP) {
$value = ldap_pear_escape_dn_value($value);
}
switch ($context) {
case LDAP_SERVER_MASSAGE_DISPLAY:
case LDAP_SERVER_MASSAGE_TOKEN_REPLACE:
case LDAP_SERVER_MASSAGE_QUERY_LDAP:
case LDAP_SERVER_MASSAGE_QUERY_DB:
case LDAP_SERVER_MASSAGE_QUERY_ARRAY:
case LDAP_SERVER_MASSAGE_QUERY_PROPERTY:
case LDAP_SERVER_MASSAGE_STORE_LDAP:
case LDAP_SERVER_MASSAGE_STORE_DB:
case LDAP_SERVER_MASSAGE_STORE_ARRAY:
case LDAP_SERVER_MASSAGE_STORE_PROPERTY:
break;
}
}
elseif ($value_type == 'attr_name') {
// attr_name
switch ($context) {
case LDAP_SERVER_MASSAGE_DISPLAY:
break;
case LDAP_SERVER_MASSAGE_TOKEN_REPLACE:
case LDAP_SERVER_MASSAGE_QUERY_LDAP:
case LDAP_SERVER_MASSAGE_QUERY_DB:
case LDAP_SERVER_MASSAGE_QUERY_ARRAY:
case LDAP_SERVER_MASSAGE_QUERY_PROPERTY:
case LDAP_SERVER_MASSAGE_STORE_LDAP:
case LDAP_SERVER_MASSAGE_STORE_DB:
case LDAP_SERVER_MASSAGE_STORE_ARRAY:
case LDAP_SERVER_MASSAGE_STORE_PROPERTY:
if ($scalar) {
$value = drupal_strtolower($value);
}
else {
foreach ($value as $i => $val) {
$value[$i] = drupal_strtolower($val);
}
}
break;
}
}
return $value;
}
/**
* from pear net_ldap2-2.0.11
*
* Escapes the given VALUES according to RFC 2254 so that they can be safely used in LDAP filters.
*
* Any control characters with an ACII code < 32 as well as the characters with special meaning in
* LDAP filters "*", "(", ")", and "\" (the backslash) are converted into the representation of a
* backslash followed by two hex digits representing the hexadecimal value of the character.
*
* @param array $values Array of values to escape
*
* @static
* @return array Array $values, but escaped
*/
function ldap_pear_escape_filter_value($values = array()) {
// Parameter validation
$is_scalar = is_scalar($values);
if (!is_array($values)) {
$values = array(
$values,
);
}
foreach ($values as $key => $val) {
// Escaping of filter meta characters
$val = str_replace('\\', '\\5c', $val);
$val = str_replace('*', '\\2a', $val);
$val = str_replace('(', '\\28', $val);
$val = str_replace(')', '\\29', $val);
// ASCII < 32 escaping
$val = ldap_pear_asc2hex32($val);
if (null === $val) {
$val = '\\0';
}
// apply escaped "null" if string is empty
$values[$key] = $val;
}
return $is_scalar ? $values[0] : $values;
}
/**
* Undoes the conversion done by {@link escape_filter_value()}.
*
* Converts any sequences of a backslash followed by two hex digits into the corresponding character.
*
* @param array $values Array of values to escape
*
* @static
* @return array Array $values, but unescaped
*/
function ldap_pear_unescape_filter_value($values = array()) {
// Parameter validation
$is_scalar = is_scalar($values);
if (!is_array($values)) {
$values = array(
$values,
);
}
foreach ($values as $key => $value) {
// Translate hex code into ascii
$values[$key] = ldap_pear_hex2asc($value);
}
return $is_scalar ? $values[0] : $values;
}
/**
* Escapes a DN value according to RFC 2253
*
* Escapes the given VALUES according to RFC 2253 so that they can be safely used in LDAP DNs.
* The characters ",", "+", """, "\", "<", ">", ";", "#", "=" with a special meaning in RFC 2252
* are preceeded by ba backslash. Control characters with an ASCII code < 32 are represented as \hexpair.
* Finally all leading and trailing spaces are converted to sequences of \20.
*
* @param array $values An array containing the DN values that should be escaped
*
* @static
* @return array The array $values, but escaped
*/
function ldap_pear_escape_dn_value($values = array()) {
// Parameter validation
$is_scalar = is_scalar($values);
if (!is_array($values)) {
$values = array(
$values,
);
}
foreach ($values as $key => $val) {
// Escaping of filter meta characters
$val = str_replace('\\', '\\\\', $val);
$val = str_replace(',', '\\,', $val);
$val = str_replace('+', '\\+', $val);
$val = str_replace('"', '\\"', $val);
$val = str_replace('<', '\\<', $val);
$val = str_replace('>', '\\>', $val);
$val = str_replace(';', '\\;', $val);
$val = str_replace('#', '\\#', $val);
$val = str_replace('=', '\\=', $val);
// ASCII < 32 escaping
$val = ldap_pear_asc2hex32($val);
// Convert all leading and trailing spaces to sequences of \20.
if (preg_match('/^(\\s*)(.+?)(\\s*)$/', $val, $matches)) {
$val = $matches[2];
for ($i = 0; $i < strlen($matches[1]); $i++) {
$val = '\\20' . $val;
}
for ($i = 0; $i < strlen($matches[3]); $i++) {
$val = $val . '\\20';
}
}
if (null === $val) {
$val = '\\0';
}
// apply escaped "null" if string is empty
$values[$key] = $val;
}
return $is_scalar ? $values[0] : $values;
}
/**
* Undoes the conversion done by escape_dn_value().
*
* Any escape sequence starting with a baskslash - hexpair or special character -
* will be transformed back to the corresponding character.
*
* @param array $values Array of DN Values
*
* @return array Same as $values, but unescaped
* @static
*/
function ldap_pear_unescape_dn_value($values = array()) {
$is_scalar = is_scalar($values);
// Parameter validation
if (!is_array($values)) {
$values = array(
$values,
);
}
foreach ($values as $key => $val) {
// strip slashes from special chars
$val = str_replace('\\\\', '\\', $val);
$val = str_replace('\\,', ',', $val);
$val = str_replace('\\+', '+', $val);
$val = str_replace('\\"', '"', $val);
$val = str_replace('\\<', '<', $val);
$val = str_replace('\\>', '>', $val);
$val = str_replace('\\;', ';', $val);
$val = str_replace('\\#', '#', $val);
$val = str_replace('\\=', '=', $val);
// Translate hex code into ascii
$values[$key] = ldap_pear_hex2asc($val);
}
return $is_scalar ? $values[0] : $values;
}
/**
* Converts all Hex expressions ("\HEX") to their original ASCII characters
*
* @param string $string String to convert
*
* @static
* @return string
*/
function ldap_pear_hex2asc($string) {
$string = preg_replace_callback("/\\\\([0-9A-Fa-f]{2})/", function (array $matches) {
return chr(hexdec($matches[0]));
}, $string);
return $string;
}
/**
* Converts all ASCII chars < 32 to "\HEX"
*
* @param string $string String to convert
*
* @static
* @return string
*/
function ldap_pear_asc2hex32($string) {
for ($i = 0; $i < strlen($string); $i++) {
$char = substr($string, $i, 1);
if (ord($char) < 32) {
$hex = dechex(ord($char));
if (strlen($hex) == 1) {
$hex = '0' . $hex;
}
$string = str_replace($char, '\\' . $hex, $string);
}
}
return $string;
}
Functions
Name | Description |
---|---|
ldap_badattr | this attempts to find bad dns, but should only be used as warningswe as the ldap spec allows for any old character to be escaped and ldap implementations may not follow the spec. |
ldap_baddn | this attempts to find bad dns, but should only be used as warningswe as the ldap spec allows for any old character to be escaped and ldap implementations may not follow the spec. |
ldap_create_drupal_account | |
ldap_password_modify | Modify a password |
ldap_pear_asc2hex32 | Converts all ASCII chars < 32 to "\HEX" |
ldap_pear_escape_dn_value | Escapes a DN value according to RFC 2253 |
ldap_pear_escape_filter_value | from pear net_ldap2-2.0.11 |
ldap_pear_hex2asc | Converts all Hex expressions ("\HEX") to their original ASCII characters |
ldap_pear_unescape_dn_value | Undoes the conversion done by escape_dn_value(). |
ldap_pear_unescape_filter_value | Undoes the conversion done by {@link escape_filter_value()}. |
ldap_servers_show_sample_user_tokens | |
ldap_server_check_plain | |
ldap_server_massage_text | function to massage (change case, escape, unescape) ldap attribute names and values. The primary purpose of this is to articulate and ensure consistency across ldap modules. |
ldap_server_module_load_include | @file collection of functions that don't belong in server object |
ldap_server_tokenize_entry | Turn an ldap entry into a token array suitable for the t() function |
ldap_server_tokens_needed_for_template | |
ldap_server_token_replace | |
ldap_user_modify | Modify an LDAP Entry |