packages/ssoinabox-webui/root/usr/local/share/ssoinabox/htdocs/includes/ldap.php
author Dan Fuhry <dan@fuhry.us>
Sat, 16 Feb 2013 20:47:45 -0500
changeset 7 faf6f7941e8f
parent 4 2212b2ded8bf
child 8 f68fdcc18df9
permissions -rw-r--r--
Enhancements to User CP - handle attribute deletion/mod more gracefully

<?php

// BEGIN CONSTANTS

$ldap_readonly_attrs = array(
		'uid'
		, 'objectClass'
		, 'userPassword'
		, 'homeDirectory'
		, 'uidNumber'
		, 'gidNumber'
	);

$ldap_field_names = array(
		'cn' => 'Common name'
		, 'uid' => 'Username'
		, 'givenName' => 'Given name'
		, 'sn' => 'Surname'
		, 'mail' => 'E-mail'
		, 'title' => 'Job title'
		, 'telephoneNumber' => 'Phone'
	);

$ldap_add_single = array(
		'title'
		, 'mail'
	);

$ldap_add_multiple = array(
		'telephoneNumber'
		, 'mobile'
		, 'mail'
		, 'sshPublicKey'
	);

// END CONSTANTS

global $_ldapconn;
$_ldapconn = ldap_connect($ldap_server);
if ( !$_ldapconn )
	die("Failed to connect to the LDAP database");

if ( !ldap_set_option($_ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3) )
	die("Failed to set LDAP version to 3");

if ( !ldap_bind($_ldapconn, $ldap_manager['dn'], $ldap_manager['password']) )
	die("Failed to bind to LDAP as a manager");

register_shutdown_function(function() use ($_ldapconn)
	{
		ldap_unbind($_ldapconn);
	});


function ldap_escape($str)
{
	// FIXME escape properly
	return $str;
}

function ldap_get_user($username)
{
	global $_ldapconn, $ldap_user_basedn;
	
	$search_filter = sprintf("(&(uid=%s)(objectClass=posixAccount))", ldap_escape($username));
	
	$search_result = ldap_search($_ldapconn, $ldap_user_basedn, $search_filter);
	if ( ldap_count_entries($_ldapconn, $search_result) !== 1 )
		return false;
	
	return ldap_array_cleanup(ldap_get_attributes($_ldapconn, ldap_first_entry($_ldapconn, $search_result)));
}

function ldap_get_group($group)
{
	global $_ldapconn, $ldap_group_basedn;
	
	$search_filter = sprintf("(&(cn=%s)(objectClass=posixGroup))", ldap_escape($group));
	
	$search_result = ldap_search($_ldapconn, $ldap_group_basedn, $search_filter);
	if ( ldap_count_entries($_ldapconn, $search_result) !== 1 )
		return false;
	
	$result = ldap_array_cleanup(ldap_get_attributes($_ldapconn, ldap_first_entry($_ldapconn, $search_result)));
	if ( !isset($result['memberUid']) )
		$result['memberUid'] = array();
	if ( !is_array($result['memberUid']) )
		$result['memberUid'] = array($result['memberUid']);
	
	return $result;
}

function ldap_update_user($user, $entry)
{
	global $_ldapconn;
	
	return ldap_modify($_ldapconn, ldap_make_user_dn($user), $entry);
}

function ldap_list_users()
{
	global $_ldapconn, $ldap_user_basedn;
	
	$search_result = ldap_search($_ldapconn, $ldap_user_basedn, '(objectClass=organizationalPerson)');
	
	$results = array();
	for ( $entry = ldap_first_entry($_ldapconn, $search_result);
			$entry;
			$entry = ldap_next_entry($_ldapconn, $entry) )
	{
		$entry_arr = ldap_array_cleanup(ldap_get_attributes($_ldapconn, $entry));
		$results[$entry_arr['uid']] = $entry_arr;
	}
	
	return $results;
}

function ldap_list_groups()
{
	global $_ldapconn, $ldap_group_basedn;
	
	$search_result = ldap_search($_ldapconn, $ldap_group_basedn, '(objectClass=posixGroup)');
	
	$results = array();
	for ( $entry = ldap_first_entry($_ldapconn, $search_result);
			$entry;
			$entry = ldap_next_entry($_ldapconn, $entry) )
	{
		$entry_arr = ldap_array_cleanup(ldap_get_attributes($_ldapconn, $entry));
		$results[$entry_arr['cn']] = $entry_arr;
	}
	
	return $results;
}

function ldap_array_cleanup($arr)
{
	global $ldap_add_multiple;
	
	$result = array();
	foreach ( $arr as $k => $v )
	{
		if ( is_int($k) || $k == 'count' )
			continue;
		
		if ( $v['count'] === 1 && !in_array($k, $ldap_add_multiple) )
			$v = $v[0];
		else
			unset($v['count']);
		
		$result[$k] = $v;
	}
	
	return $result;
}

function ldap_make_user_dn($username)
{
	global $ldap_user_basedn;
	return sprintf('uid=%s,%s', ldap_escape($username), $ldap_user_basedn);
}

function ldap_make_group_dn($group)
{
	global $ldap_group_basedn;
	return sprintf('cn=%s,%s', ldap_escape($group), $ldap_group_basedn);
}

function ldap_replace_attr($dn, $attribute, $value)
{
	global $_ldapconn;
	
	$ldif = array(
			$attribute => array($value)
		);
	
	return ldap_mod_replace($_ldapconn, $dn, $ldif);
}

function ldap_delete_user($username)
{
	global $_ldapconn, $ldap_user_basedn, $ldap_group_basedn;
	
	// remove user from all LDAP groups
	$search_filter = sprintf("(&(memberUid=%s)(objectClass=posixGroup))", ldap_escape($username));
	$search_result = ldap_search($_ldapconn, $ldap_group_basedn, $search_filter);
	for ( $entry = ldap_first_entry($_ldapconn, $search_result);
			$entry;
			$entry = ldap_next_entry($_ldapconn, $entry) )
	{
		$entry_arr = ldap_array_cleanup(ldap_get_attributes($_ldapconn, $entry));
		$dn = ldap_get_dn($_ldapconn, $entry);
		ldap_mod_del($_ldapconn, $dn, array('memberUid' => array($username)));
	}
	
	// delete user DN
	return ldap_delete($_ldapconn, ldap_make_user_dn($username));
}

function ldap_delete_group_member($gid, $uid)
{
	global $_ldapconn;
	
	return ldap_mod_del($_ldapconn, ldap_make_group_dn($gid), array('memberUid' => array($uid)));
}

function ldap_add_group_member($gid, $uid)
{
	global $_ldapconn;
	
	return ldap_mod_add($_ldapconn, ldap_make_group_dn($gid), array('memberUid' => array($uid)));
}

function get_next_available_uid()
{
	$users = ldap_list_users();
	$uids = array();
	foreach ( $users as $u )
		$uids[] = intval($u['uidNumber']);
	
	asort($uids);
	$uid = UID_MIN;
	$last_uid = $uids[0];
	foreach ( $uids as $u )
	{
		if ( $u > $last_uid + 1 && ($last_uid + 1) > UID_MIN )
		{
			return $last_uid + 1;
		}
		
		$last_uid = $u;
	}
	
	return max($uids) + 1;
}

function get_next_available_gid()
{
	$groups = ldap_list_groups();
	$gids = array();
	foreach ( $groups as $g )
		$gids[] = intval($g['gidNumber']);
	
	asort($gids);
	$gid = GID_MIN;
	$last_gid = $gids[0];
	foreach ( $gids as $g )
	{
		if ( $g > $last_gid + 1 && ($last_gid + 1) > GID_MIN )
		{
			return $last_gid + 1;
		}
		
		$last_gid = $g;
	}
	
	return max($gids) + 1;
}

function get_next_available_extension()
{
	$users = ldap_list_users();
	$exts = array();
	foreach ( $users as $u )
	{
		if ( !isset($u['telephoneNumber']) )
			continue;
		
		if ( !is_array($u['telephoneNumber']) )
			$u['telephoneNumber'] = array($u['telephoneNumber']);
		
		foreach ( $u['telephoneNumber'] as $n )
		{
			if ( preg_match('/^([0-9]+) \(extension\)$/', $n, $match) )
				$exts[] = intval($n);
		}
	}
	
	asort($exts);
	$ext = PHONE_EXT_MIN;
	$last_ext = PHONE_EXT_MIN - 1;
	foreach ( $exts as $e )
	{
		if ( $e > $last_ext + 1 && ($last_ext + 1) > UID_MIN )
		{
			return $last_ext + 1;
		}
		
		$last_ext = $e;
	}
	
	return count($exts) ? max($exts) + 1 : PHONE_EXT_MIN;
}

function ldap_create_user($username, $gn, $sn, $cn, $title)
{
	global $_ldapconn;
	
	$krb_realm = get_default_kerberos_realm();
	
	if ( !ldap_add($_ldapconn, ldap_make_user_dn($username), array(
			'cn' => array($cn)
			, 'uid' => array($username)
			, 'objectClass' => array(
				'top'
				, 'person'
				, 'inetOrgPerson'
				, 'organizationalPerson'
				, 'posixAccount'
				, 'ldapPublicKey'
				)
			, 'gn' => array($gn)
			, 'sn' => array($sn)
			, 'userPassword' => array("{SASL}$username@$krb_realm")
			, 'loginShell' => array('/bin/bash')
			, 'homeDirectory' => array("/home/users/$username")
			, 'uidNumber' => array(get_next_available_uid())
			, 'gidNumber' => array(500)
			, 'title' => array($title)
		)) )
		return false;
	
	if ( !ldap_mod_add($_ldapconn, ldap_make_group_dn('users'), array('memberUid' => array($username))) )
		return false;
	
	return true;
}

function ldap_create_group($cn, $description)
{
	global $_ldapconn;
	
	if ( !ldap_add($_ldapconn, ldap_make_group_dn($cn), array(
			'cn' => array($cn)
			, 'description' => array($description)
			, 'gidNumber' => array(get_next_available_gid())
			, 'objectClass' => array(
				'top'
				, 'posixGroup'
				)
		)) )
		return false;
}

function ldap_delete_group($cn)
{
	global $_ldapconn;
	
	$group = ldap_get_group($cn);
	$users = ldap_list_users();
	
	foreach ( $users as $u )
	{
		if ( $u['gidNumber'] === $group['gidNumber'] )
			return false;
	}
	
	return ldap_delete($_ldapconn, ldap_make_group_dn($cn));
}

/**
 * Is the given username in the specified LDAP group?
 * @param string username
 * @param string Group name
 * @return bool
 */

function ldap_test_group_membership($username, $group)
{
	global $_ldapconn, $ldap_group_basedn;
	
	$filter = sprintf('(&(memberUid=%s)(cn=%s)(objectClass=posixGroup))', ldap_escape($username), ldap_escape($group));
	
	$result = ldap_search($_ldapconn, $ldap_group_basedn, $filter);
	return ldap_count_entries($_ldapconn, $result) > 0;
}