accounts addressbook incl. working updates for active directory

This commit is contained in:
Ralf Becker 2013-06-01 17:55:33 +00:00
parent 5c63214e82
commit d328af7cff
5 changed files with 273 additions and 116 deletions

View File

@ -12,12 +12,15 @@
/** /**
* Active directory backend for accounts (not yet AD contacts) * Active directory backend for accounts (not yet AD contacts)
* *
* We use ADS objectGUID as contact ID and UID. * We use ADS string representation of objectGUID as contact ID and UID.
*
* Unfortunatly Samba4 and active directory of win2008r2 differn on how to search for an objectGUID:
* - Samba4 can only search for string representation eg. (objectGUID=2336A3FC-EDBD-42A2-9EEB-BD7A5DD2804E)
* - win2008r2 can only search for hex representation eg. (objectGUID=\FC\A3\36\23\BD\ED\A2\42\9E\EB\BD\7A\5D\D2\80\4E)
* We could use both filters or-ed together, for now we detect Samba4 and use string GUID for it.
* *
* All values used to construct filters need to run through ldap::quote(), * All values used to construct filters need to run through ldap::quote(),
* to be save against LDAP query injection!!! * to be save against LDAP query injection!!!
*
* @todo get saving of contacts working: fails while checking of container exists ...
*/ */
class addressbook_ads extends addressbook_ldap class addressbook_ads extends addressbook_ldap
{ {
@ -37,6 +40,13 @@ class addressbook_ads extends addressbook_ldap
*/ */
var $accountsFilter = '(objectclass=user)'; var $accountsFilter = '(objectclass=user)';
/**
* Attribute used for DN
*
* @var string
*/
var $dn_attribute='cn';
/** /**
* Accounts ADS object * Accounts ADS object
* *
@ -44,6 +54,14 @@ class addressbook_ads extends addressbook_ldap
*/ */
protected $accounts_ads; protected $accounts_ads;
/**
* ADS is Samba4 (true), otherwise false
*
* @var boolean
*/
public $is_samba4 = false;
/** /**
* constructor of the class * constructor of the class
* *
@ -78,6 +96,7 @@ class addressbook_ads extends addressbook_ldap
$this->connect(); $this->connect();
} }
$this->ldapServerInfo = ldapserverinfo::get($this->ds, $this->ldap_config['ads_host']); $this->ldapServerInfo = ldapserverinfo::get($this->ds, $this->ldap_config['ads_host']);
$this->is_samba4 = $this->ldapServerInfo->serverType == SAMBA4_LDAPSERVER;
// AD seems to use user, instead of inetOrgPerson // AD seems to use user, instead of inetOrgPerson
$this->schema2egw['user'] = $this->schema2egw['inetorgperson']; $this->schema2egw['user'] = $this->schema2egw['inetorgperson'];
@ -106,6 +125,25 @@ class addressbook_ads extends addressbook_ldap
$this->ds = $this->accounts_ads->ldap_connection(); $this->ds = $this->accounts_ads->ldap_connection();
} }
/**
* Return LDAP filter for contact id
*
* @param string $contact_id
* @return string
*/
protected function id_filter($contact_id)
{
// check that GUID eg. from URL contains only valid hex characters and dash
// we cant use ldap::quote() for win2008r2 hex GUID, as it contains backslashes
if (!preg_match('/^[0-9A-Fa-f-]+/', $contact_id))
{
throw new egw_exception_assertion_failed("'$contact_id' is NOT a valid GUID!");
}
// samba4 can only search by string representation of objectGUID, while win2008r2 requires hex representation
return '(objectguid='.($this->is_samba4 ? $contact_id : $this->accounts_ads->objectguid2hex($contact_id)).')';
}
/** /**
* reads contact data * reads contact data
* *
@ -120,11 +158,11 @@ class addressbook_ads extends addressbook_ldap
$account_id = (int)(is_array($contact_id) ? $contact_id['account_id'] : substr($contact_id,8)); $account_id = (int)(is_array($contact_id) ? $contact_id['account_id'] : substr($contact_id,8));
$contact_id = $GLOBALS['egw']->accounts->id2name($account_id, 'person_id'); $contact_id = $GLOBALS['egw']->accounts->id2name($account_id, 'person_id');
} }
$contact_id = ldap::quote(!is_array($contact_id) ? $contact_id : $contact_id = !is_array($contact_id) ? $contact_id :
(isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid'])); (isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid']);
$rows = $this->_searchLDAP($this->allContactsDN, "(objectguid=$contact_id)", $this->all_attributes, ADDRESSBOOK_ALL);
$rows = $this->_searchLDAP($this->allContactsDN, $filter=$this->id_filter($contact_id), $this->all_attributes, ADDRESSBOOK_ALL);
//error_log(__METHOD__."('$contact_id') _searchLDAP($this->allContactsDN, '$filter',...)=".array2string($rows));
return $rows ? $rows[0] : false; return $rows ? $rows[0] : false;
} }
@ -147,4 +185,19 @@ class addressbook_ads extends addressbook_ldap
$this->_inetorgperson2egw($contact, $data); $this->_inetorgperson2egw($contact, $data);
} }
/**
* Remove attributes we are not allowed to update
*
* @param array $attributes
*/
function sanitize_update(array &$ldapContact)
{
// not allowed and not need to update these in AD
unset($ldapContact['objectguid']);
unset($ldapContact['objectsid']);
parent::sanitize_update($ldapContact);
}
} }

View File

@ -75,6 +75,13 @@ class addressbook_ldap
*/ */
var $allContactsDN; var $allContactsDN;
/**
* Attribute used for DN
*
* @var string
*/
var $dn_attribute='uid';
/** /**
* @var int $total holds the total count of found rows * @var int $total holds the total count of found rows
*/ */
@ -237,6 +244,13 @@ class addressbook_ldap
*/ */
private $ldap_config; private $ldap_config;
/**
* LDAP connection
*
* @var resource
*/
var $ds;
/** /**
* constructor of the class * constructor of the class
* *
@ -256,10 +270,10 @@ class addressbook_ldap
{ {
$this->ldap_config =& $GLOBALS['egw_info']['server']; $this->ldap_config =& $GLOBALS['egw_info']['server'];
} }
$this->personalContactsDN = 'ou=personal,ou=contacts,'. $this->ldap_config['ldap_contact_context'];
$this->sharedContactsDN = 'ou=shared,ou=contacts,'. $this->ldap_config['ldap_contact_context'];
$this->accountContactsDN = $this->ldap_config['ldap_context']; $this->accountContactsDN = $this->ldap_config['ldap_context'];
$this->allContactsDN = $this->ldap_config['ldap_contact_context']; $this->allContactsDN = $this->ldap_config['ldap_contact_context'];
$this->personalContactsDN = 'ou=personal,ou=contacts,'. $this->allContactsDN;
$this->sharedContactsDN = 'ou=shared,ou=contacts,'. $this->allContactsDN;
if ($ds) if ($ds)
{ {
@ -303,7 +317,7 @@ class addressbook_ldap
elseif (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap) elseif (substr($GLOBALS['egw_info']['server']['contact_repository'],-4) != 'ldap') // not (ldap or sql-ldap)
{ {
$this->ldap_config['ldap_contact_host'] = $this->ldap_config['ldap_host']; $this->ldap_config['ldap_contact_host'] = $this->ldap_config['ldap_host'];
$this->ldap_config['ldap_contact_context'] = $this->ldap_config['ldap_context']; $this->allContactsDN = $this->ldap_config['ldap_context'];
$this->ds = $GLOBALS['egw']->ldap->ldapConnect(); $this->ds = $GLOBALS['egw']->ldap->ldapConnect();
} }
else else
@ -340,6 +354,37 @@ class addressbook_ldap
return array_values(array_unique($fields)); return array_values(array_unique($fields));
} }
/**
* Return LDAP filter for contact id
*
* @param string $id
* @return string
*/
protected function id_filter($id)
{
return '(|(entryUUID='.ldap::quote($id).')(uid='.ldap::quote($id).'))';
}
/**
* Return LDAP filter for (multiple) contact ids
*
* @param array|string $ids
* @return string
*/
protected function ids_filter($ids)
{
if (!is_array($ids) || count($ids) == 1)
{
return $this->id_filter(is_array($ids) ? array_shift($ids) : $ids);
}
$filter = array();
foreach($ids as $id)
{
$filter[] = $this->id_filter($id);
}
return '(|'.implode('', $filter).')';
}
/** /**
* reads contact data * reads contact data
* *
@ -355,16 +400,30 @@ class addressbook_ldap
} }
else else
{ {
$contact_id = ldap::quote(!is_array($contact_id) ? $contact_id : if (is_array($contact_id)) $contact_id = isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid'];
(isset ($contact_id['id']) ? $contact_id['id'] : $contact_id['uid'])); $filter = $this->id_filter($contact_id);
$filter = "(|(entryUUID=$contact_id)(uid=$contact_id))";
} }
$rows = $this->_searchLDAP($this->ldap_config['ldap_contact_context'], $rows = $this->_searchLDAP($this->allContactsDN,
$filter, $this->all_attributes, ADDRESSBOOK_ALL); $filter, $this->all_attributes, ADDRESSBOOK_ALL);
return $rows ? $rows[0] : false; return $rows ? $rows[0] : false;
} }
/**
* Remove attributes we are not allowed to update
*
* @param array $attributes
*/
function sanitize_update(array &$ldapContact)
{
// never allow to change the uidNumber (account_id) on update, as it could be misused by eg. xmlrpc or syncml
unset($ldapContact['uidnumber']);
unset($ldapContact['entryuuid']); // not allowed to modify that, no need either
unset($ldapContact['objectClass']);
}
/** /**
* saves the content of data to the db * saves the content of data to the db
* *
@ -403,7 +462,7 @@ class addressbook_ldap
$baseDN = $this->accountContactsDN; $baseDN = $this->accountContactsDN;
$cn = false; $cn = false;
// we need an admin connection // we need an admin connection
$this->ds = $this->connect(true); $this->connect(true);
// for sql-ldap we need to account_lid/uid as id, NOT the contact_id in id! // for sql-ldap we need to account_lid/uid as id, NOT the contact_id in id!
if ($GLOBALS['egw_info']['server']['contact_repository'] == 'sql-ldap') if ($GLOBALS['egw_info']['server']['contact_repository'] == 'sql-ldap')
@ -416,7 +475,6 @@ class addressbook_ldap
error_log("Permission denied, to write: data[owner]=$data[owner], data[account_id]=$data[account_id], account_id=".$GLOBALS['egw_info']['user']['account_id']); error_log("Permission denied, to write: data[owner]=$data[owner], data[account_id]=$data[account_id], account_id=".$GLOBALS['egw_info']['user']['account_id']);
return lang('Permission denied !!!'); // only admin or the user itself is allowd to write accounts! return lang('Permission denied !!!'); // only admin or the user itself is allowd to write accounts!
} }
// check if $baseDN exists. If not create it // check if $baseDN exists. If not create it
if (($err = $this->_check_create_dn($baseDN))) if (($err = $this->_check_create_dn($baseDN)))
{ {
@ -424,11 +482,11 @@ class addressbook_ldap
} }
// check the existing objectclasses of an entry, none = array() for new ones // check the existing objectclasses of an entry, none = array() for new ones
$oldObjectclasses = array(); $oldObjectclasses = array();
$attributes = array('dn','cn','objectClass','uid','mail'); $attributes = array('dn','cn','objectClass',$this->dn_attribute,'mail');
$contactUID = $this->data[$this->contacts_id]; $contactUID = $this->data[$this->contacts_id];
if(!empty($contactUID) && if (!empty($contactUID) &&
($result = ldap_search($this->ds, $this->ldap_config['ldap_contact_context'], ($result = ldap_search($this->ds, $base=$this->allContactsDN, $filter=$this->id_filter($contactUID), $attributes)) &&
'(|(entryUUID='.ldap::quote($contactUID).')(uid='.ldap::quote($contactUID).'))', $attributes)) &&
($oldContactInfo = ldap_get_entries($this->ds, $result)) && $oldContactInfo['count']) ($oldContactInfo = ldap_get_entries($this->ds, $result)) && $oldContactInfo['count'])
{ {
unset($oldContactInfo[0]['objectclass']['count']); unset($oldContactInfo[0]['objectclass']['count']);
@ -438,13 +496,12 @@ class addressbook_ldap
} }
$isUpdate = true; $isUpdate = true;
} }
if(!$contactUID)
if(empty($contactUID))
{ {
$this->data[$this->contacts_id] = $contactUID = md5($GLOBALS['egw']->common->randomstring(15)); $ldapContact[$this->contacts_id] = $this->data[$this->contacts_id] = $contactUID = md5($GLOBALS['egw']->common->randomstring(15));
} }
//error_log(__METHOD__."() contactUID='$contactUID', isUpdate=".array2string($isUpdate).", oldContactInfo=".array2string($oldContactInfo));
$ldapContact['uid'] = $contactUID;
// add for all supported objectclasses the objectclass and it's attributes // add for all supported objectclasses the objectclass and it's attributes
foreach($this->schema2egw as $objectclass => $mapping) foreach($this->schema2egw as $objectclass => $mapping)
{ {
@ -484,7 +541,7 @@ class addressbook_ldap
$this->$egw2objectclass($ldapContact,$data,$isUpdate); $this->$egw2objectclass($ldapContact,$data,$isUpdate);
} }
} }
if($isUpdate) if ($isUpdate)
{ {
// make sure multiple email-addresses in the mail attribute "survive" // make sure multiple email-addresses in the mail attribute "survive"
if (isset($ldapContact['mail']) && $oldContactInfo[0]['mail']['count'] > 1) if (isset($ldapContact['mail']) && $oldContactInfo[0]['mail']['count'] > 1)
@ -497,9 +554,6 @@ class addressbook_ldap
// update entry // update entry
$dn = $oldContactInfo[0]['dn']; $dn = $oldContactInfo[0]['dn'];
$needRecreation = false; $needRecreation = false;
// never allow to change the uidNumber (account_id) on update, as it could be misused by eg. xmlrpc or syncml
unset($ldapContact['uidnumber']);
unset($ldapContact['entryuuid']); // not allowed to modify that, no need either
// add missing objectclasses // add missing objectclasses
if($ldapContact['objectClass'] && array_diff($ldapContact['objectClass'],$oldObjectclasses)) if($ldapContact['objectClass'] && array_diff($ldapContact['objectClass'],$oldObjectclasses))
@ -522,9 +576,9 @@ class addressbook_ldap
} }
// check if we need to rename the DN or need to recreate the contact // check if we need to rename the DN or need to recreate the contact
$newRDN = 'uid='. ldap::quote($contactUID); $newRDN = $this->dn_attribute.'='. ldap::quote($ldapContact[$this->dn_attribute]);
$newDN = $newRDN .','. $baseDN; $newDN = $newRDN .','. $baseDN;
if(strtolower($dn) != strtolower($newDN) || $needRecreation) if ($needRecreation)
{ {
$result = ldap_read($this->ds, $dn, 'objectclass=*'); $result = ldap_read($this->ds, $dn, 'objectclass=*');
$oldContact = ldap_get_entries($this->ds, $result); $oldContact = ldap_get_entries($this->ds, $result);
@ -536,7 +590,7 @@ class addressbook_ldap
$newContact[$key] = $value; $newContact[$key] = $value;
} }
} }
$newContact['uid'] = $contactUID; $newContact[$this->dn_attribute] = $ldapContact[$this->dn_attribute];
if(is_array($ldapContact['objectClass']) && count($ldapContact['objectClass']) > 0) if(is_array($ldapContact['objectClass']) && count($ldapContact['objectClass']) > 0)
{ {
@ -559,7 +613,21 @@ class addressbook_ldap
} }
$dn = $newDN; $dn = $newDN;
} }
unset($ldapContact['objectClass']); // try renaming entry if content of dn-attribute changed
if (strtolower($dn) != strtolower($newDN) || $ldapContact[$this->dn_attribute] != $oldContactInfo[$this->dn_attribute])
{
if (@ldap_rename($this->ds, $dn, $newRDN, null, true))
{
$dn = $newDN;
}
else
{
error_log(__METHOD__."() ldap_rename or $dn to $newRDN failed! ".ldap_error($this->ds));
}
}
unset($ldapContact[$this->dn_attribute]);
$this->sanitize_update($ldapContact);
if (!@ldap_modify($this->ds, $dn, $ldapContact)) if (!@ldap_modify($this->ds, $dn, $ldapContact))
{ {
@ -571,7 +639,7 @@ class addressbook_ldap
} }
else else
{ {
$dn = 'uid='. ldap::quote($ldapContact['uid']) .','. $baseDN; $dn = $this->dn_attribute.'='. ldap::quote($ldapContact[$this->dn_attribute]) .','. $baseDN;
unset($ldapContact['entryuuid']); // trying to write it, gives an error unset($ldapContact['entryuuid']); // trying to write it, gives an error
if (!@ldap_add($this->ds, $dn, $ldapContact)) if (!@ldap_add($this->ds, $dn, $ldapContact))
@ -608,7 +676,7 @@ class addressbook_ldap
foreach($keys as $entry) foreach($keys as $entry)
{ {
$entry = ldap::quote(is_array($entry) ? $entry['id'] : $entry); $entry = ldap::quote(is_array($entry) ? $entry['id'] : $entry);
if($result = ldap_search($this->ds, $this->ldap_config['ldap_contact_context'], if($result = ldap_search($this->ds, $this->allContactsDN,
"(|(entryUUID=$entry)(uid=$entry))", $attributes)) "(|(entryUUID=$entry)(uid=$entry))", $attributes))
{ {
$contactInfo = ldap_get_entries($this->ds, $result); $contactInfo = ldap_get_entries($this->ds, $result);
@ -715,6 +783,11 @@ class addressbook_ldap
$searchFilter = ''; $searchFilter = '';
foreach($criteria as $egwSearchKey => $searchValue) foreach($criteria as $egwSearchKey => $searchValue)
{ {
if (in_array($egwSearchKey, array('id','contact_id')))
{
$searchFilter .= $this->ids_filter($searchValue);
continue;
}
foreach($this->schema2egw as $mapping) foreach($this->schema2egw as $mapping)
{ {
if(($ldapSearchKey = $mapping[$egwSearchKey])) if(($ldapSearchKey = $mapping[$egwSearchKey]))
@ -736,7 +809,6 @@ class addressbook_ldap
} }
$colFilter = $this->_colFilter($filter); $colFilter = $this->_colFilter($filter);
$ldapFilter = "(&$objectFilter$searchFilter$colFilter)"; $ldapFilter = "(&$objectFilter$searchFilter$colFilter)";
if (!($rows = $this->_searchLDAP($searchDN, $ldapFilter, $this->all_attributes, $addressbookType))) if (!($rows = $this->_searchLDAP($searchDN, $ldapFilter, $this->all_attributes, $addressbookType)))
{ {
return $rows; return $rows;
@ -849,6 +921,11 @@ class addressbook_ldap
} }
break; break;
case 'id':
case 'contact_id':
$filter .= $this->ids_filter($value);
break;
default: default:
if (!is_int($key)) if (!is_int($key))
{ {
@ -1020,7 +1097,7 @@ class addressbook_ldap
/** /**
* check if $baseDN exists. If not create it * check if $baseDN exists. If not create it
* *
* @param string $baseDN cn=xxx,ou=yyy,ou=contacts,$this->ldap_config['ldap_contact_context'] * @param string $baseDN cn=xxx,ou=yyy,ou=contacts,$this->allContactsDN
* @return boolean/string false on success or string with error-message * @return boolean/string false on success or string with error-message
*/ */
function _check_create_dn($baseDN) function _check_create_dn($baseDN)
@ -1042,8 +1119,8 @@ class addressbook_ldap
list(,$ou) = explode(',',$baseDN); list(,$ou) = explode(',',$baseDN);
foreach(array( foreach(array(
'ou=contacts,'.$this->ldap_config['ldap_contact_context'], 'ou=contacts,'.$this->allContactsDN,
$ou.',ou=contacts,'.$this->ldap_config['ldap_contact_context'], $ou.',ou=contacts,'.$this->allContactsDN,
$baseDN, $baseDN,
) as $dn) ) as $dn)
{ {

View File

@ -29,6 +29,7 @@ require_once EGW_API_INC.'/adldap/adLDAP.php';
* @access internal only use the interface provided by the accounts class * @access internal only use the interface provided by the accounts class
* @link http://www.selfadsi.org/user-attributes-w2k8.htm * @link http://www.selfadsi.org/user-attributes-w2k8.htm
* @link http://www.selfadsi.org/attributes-e2k7.htm * @link http://www.selfadsi.org/attributes-e2k7.htm
* @link http://msdn.microsoft.com/en-us/library/ms675090(v=vs.85).aspx
*/ */
class accounts_ads class accounts_ads
{ {
@ -298,6 +299,17 @@ class accounts_ads
return $this->adldap->utilities()->decodeGuid(is_array($objectguid) ? $objectguid[0] : $objectguid); return $this->adldap->utilities()->decodeGuid(is_array($objectguid) ? $objectguid[0] : $objectguid);
} }
/**
* Convert a string GUID to hex string used in filter
*
* @param string $strGUID
* @return int
*/
public function objectguid2hex($strGUID)
{
return $this->adldap->utilities()->strGuidToHex($strGUID);
}
/** /**
* Reads the data of one account * Reads the data of one account
* *
@ -841,6 +853,8 @@ class accounts_ads
} }
if ($param['type'] == 'groups' || $param['type'] == 'both') if ($param['type'] == 'groups' || $param['type'] == 'both')
{ {
$query = ldap::quote(strtolower($param['query']));
$filter = null; $filter = null;
if(!empty($query) && $query != '*') if(!empty($query) && $query != '*')
{ {

View File

@ -193,83 +193,7 @@ class ldap
//error_log("no ldap server info found"); //error_log("no ldap server info found");
$ldapbind = @ldap_bind($this->ds, $GLOBALS['egw_info']['server']['ldap_root_dn'], $GLOBALS['egw_info']['server']['ldap_root_pw']); $ldapbind = @ldap_bind($this->ds, $GLOBALS['egw_info']['server']['ldap_root_dn'], $GLOBALS['egw_info']['server']['ldap_root_pw']);
$filter='(objectclass=*)'; $this->ldapServerInfo = ldapserverinfo::get($this->ds, $host, $supportedLDAPVersion);
$justthese = array('structuralObjectClass','namingContexts','supportedLDAPVersion','subschemaSubentry');
if(($sr = @ldap_read($this->ds, '', $filter, $justthese)))
{
if($info = ldap_get_entries($this->ds, $sr))
{
$this->ldapServerInfo = new ldapserverinfo($host);
$this->ldapServerInfo->setVersion($supportedLDAPVersion);
// check for naming contexts
if($info[0]['namingcontexts'])
{
for($i=0; $i<$info[0]['namingcontexts']['count']; $i++)
{
$namingcontexts[] = $info[0]['namingcontexts'][$i];
}
$this->ldapServerInfo->setNamingContexts($namingcontexts);
}
// check for ldap server type
if($info[0]['structuralobjectclass'])
{
switch($info[0]['structuralobjectclass'][0])
{
case 'OpenLDAProotDSE':
$ldapServerType = OPENLDAP_LDAPSERVER;
break;
default:
$ldapServerType = UNKNOWN_LDAPSERVER;
break;
}
$this->ldapServerInfo->setServerType($ldapServerType);
}
// check for subschema entry dn
if($info[0]['subschemasubentry'])
{
$subschemasubentry = $info[0]['subschemasubentry'][0];
$this->ldapServerInfo->setSubSchemaEntry($subschemasubentry);
}
// create list of supported objetclasses
if(!empty($subschemasubentry))
{
$filter='(objectclass=*)';
$justthese = array('objectClasses');
if($sr=ldap_read($this->ds, $subschemasubentry, $filter, $justthese))
{
if($info = ldap_get_entries($this->ds, $sr))
{
if($info[0]['objectclasses']) {
for($i=0; $i<$info[0]['objectclasses']['count']; $i++)
{
$pattern = '/^\( (.*) NAME \'(\w*)\' /';
if(preg_match($pattern, $info[0]['objectclasses'][$i], $matches))
{
#_debug_array($matches);
if(count($matches) == 3)
{
$supportedObjectClasses[$matches[1]] = strtolower($matches[2]);
}
}
}
$this->ldapServerInfo->setSupportedObjectClasses($supportedObjectClasses);
}
}
}
}
}
}
else
{
unset($this->ldapServerInfo);
}
$this->saveSessionData(); $this->saveSessionData();
} }

View File

@ -13,6 +13,7 @@
define('UNKNOWN_LDAPSERVER',0); define('UNKNOWN_LDAPSERVER',0);
define('OPENLDAP_LDAPSERVER',1); define('OPENLDAP_LDAPSERVER',1);
define('SAMBA4_LDAPSERVER',2);
/** /**
* Class to store and retrieve information (eg. supported object classes) of a connected ldap server * Class to store and retrieve information (eg. supported object classes) of a connected ldap server
@ -140,4 +141,92 @@ class ldapserverinfo
} }
return false; return false;
} }
/**
* Query given ldap connection for available information
*
* @param resource $ds
* @param string $host
* @param int $version 2 or 3
* @return ldapserverinfo
*/
public static function get($ds, $host, $version=3)
{
$filter='(objectclass=*)';
$justthese = array('structuralObjectClass','namingContexts','supportedLDAPVersion','subschemaSubentry','vendorname');
if(($sr = @ldap_read($ds, '', $filter, $justthese)))
{
if($info = ldap_get_entries($ds, $sr))
{
$ldapServerInfo = new ldapserverinfo($host);
$ldapServerInfo->setVersion($version);
// check for naming contexts
if($info[0]['namingcontexts'])
{
for($i=0; $i<$info[0]['namingcontexts']['count']; $i++)
{
$namingcontexts[] = $info[0]['namingcontexts'][$i];
}
$ldapServerInfo->setNamingContexts($namingcontexts);
}
// check for ldap server type
if($info[0]['structuralobjectclass'])
{
switch($info[0]['structuralobjectclass'][0])
{
case 'OpenLDAProotDSE':
$ldapServerType = OPENLDAP_LDAPSERVER;
break;
default:
$ldapServerType = UNKNOWN_LDAPSERVER;
break;
}
$ldapServerInfo->setServerType($ldapServerType);
}
if ($info[0]['vendorname'] && stripos($info[0]['vendorname'][0], 'samba') !== false)
{
$ldapServerInfo->setServerType(SAMBA4_LDAPSERVER);
}
// check for subschema entry dn
if($info[0]['subschemasubentry'])
{
$subschemasubentry = $info[0]['subschemasubentry'][0];
$ldapServerInfo->setSubSchemaEntry($subschemasubentry);
}
// create list of supported objetclasses
if(!empty($subschemasubentry))
{
$filter='(objectclass=*)';
$justthese = array('objectClasses');
if($sr=ldap_read($ds, $subschemasubentry, $filter, $justthese))
{
if($info = ldap_get_entries($ds, $sr))
{
if($info[0]['objectclasses']) {
for($i=0; $i<$info[0]['objectclasses']['count']; $i++)
{
$pattern = '/^\( (.*) NAME \'(\w*)\' /';
if(preg_match($pattern, $info[0]['objectclasses'][$i], $matches))
{
#_debug_array($matches);
if(count($matches) == 3)
{
$supportedObjectClasses[$matches[1]] = strtolower($matches[2]);
}
}
}
$ldapServerInfo->setSupportedObjectClasses($supportedObjectClasses);
}
}
}
}
}
}
return $ldapServerInfo;
}
} }