Ported the schema browser

This commit is contained in:
Deon George
2023-02-14 21:38:42 +11:00
parent 815cd49868
commit 8ec1d2b1fe
31 changed files with 2498 additions and 3093 deletions

View File

@@ -1134,859 +1134,7 @@ class ldap extends DS {
}
}
public function getRootDSE($method=null) {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
$query = array();
$query['base'] = '';
$query['scope'] = 'base';
$query['attrs'] = $this->getValue('server','root_dse_attributes');
$query['baseok'] = true;
$results = $this->query($query,$method);
if (is_array($results) && count($results) == 1)
return array_change_key_case(array_pop($results));
else
return array();
}
/** Schema Methods **/
/**
* This function will query the ldap server and request the subSchemaSubEntry which should be the Schema DN.
*
* If we cant connect to the LDAP server, we'll return false.
* If we can connect but cant get the entry, then we'll return null.
*
* @param string Which connection method resource to use
* @param dn The DN to use to obtain the schema
* @return array|false Schema if available, null if its not or false if we cant connect.
*/
private function getSchemaDN($method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
# If we already got the SchemaDN, then return it.
if ($this->_schemaDN)
return $this->_schemaDN;
if (! $this->connect($method))
return false;
$search = @ldap_read($this->connect($method),$dn,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER);
if (DEBUG_ENABLED)
debug_log('Search returned (%s)',24,0,__FILE__,__LINE__,__METHOD__,is_resource($search));
# Fix for broken ldap.conf configuration.
if (! $search && ! $dn) {
if (DEBUG_ENABLED)
debug_log('Trying to find the DN for "broken" ldap.conf',80,0,__FILE__,__LINE__,__METHOD__);
if (isset($this->_baseDN)) {
foreach ($this->_baseDN as $base) {
$search = @ldap_read($this->connect($method),$base,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER);
if (DEBUG_ENABLED)
debug_log('Search returned (%s) for base (%s)',24,0,__FILE__,__LINE__,__METHOD__,
is_resource($search),$base);
if ($search)
break;
}
}
}
if (! $search)
return null;
if (! @ldap_count_entries($this->connect($method),$search)) {
if (DEBUG_ENABLED)
debug_log('Search returned 0 entries. Returning NULL',25,0,__FILE__,__LINE__,__METHOD__);
return null;
}
$entries = @ldap_get_entries($this->connect($method),$search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$entries);
if (! $entries || ! is_array($entries))
return null;
$entry = isset($entries[0]) ? $entries[0] : false;
if (! $entry) {
if (DEBUG_ENABLED)
debug_log('Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__);
return null;
}
$sub_schema_sub_entry = isset($entry[0]) ? $entry[0] : false;
if (! $sub_schema_sub_entry) {
if (DEBUG_ENABLED)
debug_log('Sub Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__);
return null;
}
$this->_schemaDN = isset($entry[$sub_schema_sub_entry][0]) ? $entry[$sub_schema_sub_entry][0] : false;
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->_schemaDN);
return $this->_schemaDN;
}
/**
* Fetches the raw schema array for the subschemaSubentry of the server. Note,
* this function has grown many hairs to accomodate more LDAP servers. It is
* needfully complicated as it now supports many popular LDAP servers that
* don't necessarily expose their schema "the right way".
*
* Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in
* the search base if it is blank - so edit that file and comment out the BASE line.
*
* @param string Which connection method resource to use
* @param string A string indicating which type of schema to
* fetch. Five valid values: 'objectclasses', 'attributetypes',
* 'ldapsyntaxes', 'matchingruleuse', or 'matchingrules'.
* Case insensitive.
* @param dn (optional) This paremeter is the DN of the entry whose schema you
* would like to fetch. Entries have the option of specifying
* their own subschemaSubentry that points to the DN of the system
* schema entry which applies to this attribute. If unspecified,
* this will try to retrieve the schema from the RootDSE subschemaSubentry.
* Failing that, we use some commonly known schema DNs. Default
* value is the Root DSE DN (zero-length string)
* @return array an array of strings of this form:
* Array (
* [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...
* [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...
* etc.
*/
private function getRawSchema($method,$schema_to_fetch,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
$valid_schema_to_fetch = array('objectclasses','attributetypes','ldapsyntaxes','matchingrules','matchingruleuse');
if (! $this->connect($method) || $this->noconnect)
return false;
# error checking
$schema_to_fetch = strtolower($schema_to_fetch);
if (! is_null($this->_schema_entries) && isset($this->_schema_entries[$schema_to_fetch])) {
$schema = $this->_schema_entries[$schema_to_fetch];
if (DEBUG_ENABLED)
debug_log('Returning CACHED (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema);
return $schema;
}
# This error message is not localized as only developers should ever see it
if (! in_array($schema_to_fetch,$valid_schema_to_fetch))
error(sprintf('Bad parameter provided to function to %s::getRawSchema(). "%s" is not valid for the schema_to_fetch parameter.',
get_class($this),$schema_to_fetch),'error','index.php');
# Try to get the schema DN from the specified entry.
$schema_dn = $this->getSchemaDN($method,$dn);
# Do we need to try again with the Root DSE?
if (! $schema_dn && trim($dn))
$schema_dn = $this->getSchemaDN($method,'');
# Store the eventual schema retrieval in $schema_search
$schema_search = null;
if ($schema_dn) {
if (DEBUG_ENABLED)
debug_log('Using Schema DN (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_dn);
foreach (array('(objectClass=*)','(objectClass=subschema)') as $schema_filter) {
if (DEBUG_ENABLED)
debug_log('Looking for schema with Filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter);
$schema_search = @ldap_read($this->connect($method),$schema_dn,$schema_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER);
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect($method),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries);
if (is_array($schema_entries) && isset($schema_entries['count']) && $schema_entries['count']) {
if (DEBUG_ENABLED)
debug_log('Found schema with (DN:%s) (FILTER:%s) (ATTR:%s)',24,0,__FILE__,__LINE__,__METHOD__,
$schema_dn,$schema_filter,$schema_to_fetch);
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter);
unset($schema_entries);
$schema_search = null;
}
}
/* Second chance: If the DN or Root DSE didn't give us the subschemaSubentry, ie $schema_search
* is still null, use some common subSchemaSubentry DNs as a work-around. */
if (is_null($schema_search)) {
if (DEBUG_ENABLED)
debug_log('Attempting work-arounds for "broken" LDAP servers...',24,0,__FILE__,__LINE__,__METHOD__);
foreach ($this->getBaseDN() as $base) {
$ldap['W2K3 AD'][expand_dn_with_base($base,'cn=Aggregate,cn=Schema,cn=configuration,')] = '(objectClass=*)';
$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,cn=configuration,')] = '(objectClass=*)';
$ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,ou=Admin,')] = '(objectClass=*)';
}
# OpenLDAP and Novell
$ldap['OpenLDAP']['cn=subschema'] = '(objectClass=*)';
foreach ($ldap as $ldap_server_name => $ldap_options) {
foreach ($ldap_options as $ldap_dn => $ldap_filter) {
if (DEBUG_ENABLED)
debug_log('Attempting [%s] (%s) (%s)<BR>',24,0,__FILE__,__LINE__,__METHOD__,
$ldap_server_name,$ldap_dn,$ldap_filter);
$schema_search = @ldap_read($this->connect($method),$ldap_dn,$ldap_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER);
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect($method),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries);
if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) {
if (DEBUG_ENABLED)
debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter);
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter);
unset($schema_entries);
$schema_search = null;
}
if ($schema_search)
break;
}
}
# Option 3: try cn=config
$olc_schema = 'olc'.$schema_to_fetch;
$olc_schema_found = false;
if (is_null($schema_search)) {
if (DEBUG_ENABLED)
debug_log('Attempting cn=config work-around...',24,0,__FILE__,__LINE__,__METHOD__);
$ldap_dn = 'cn=schema,cn=config';
$ldap_filter = '(objectClass=*)';
$schema_search = @ldap_search($this->connect($method),$ldap_dn,$ldap_filter,array($olc_schema),false,0,10,LDAP_DEREF_NEVER);
if (! is_null($schema_search)) {
$schema_entries = @ldap_get_entries($this->connect($method),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries);
if ($schema_entries) {
if (DEBUG_ENABLED)
debug_log('Found schema with filter of (%s) and attribute filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter,$olc_schema);
$olc_schema_found = true;
} else {
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s) and attribute filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter,$olc_schema);
unset($schema_entries);
$schema_search = null;
}
}
}
if (is_null($schema_search)) {
/* Still cant find the schema, try with the RootDSE
* Attempt to pull schema from Root DSE with scope "base", or
* Attempt to pull schema from Root DSE with scope "one" (work-around for Isode M-Vault X.500/LDAP) */
foreach (array('base','one') as $ldap_scope) {
if (DEBUG_ENABLED)
debug_log('Attempting to find schema with scope (%s), filter (objectClass=*) and a blank base.',24,0,__FILE__,__LINE__,__METHOD__,
$ldap_scope);
switch ($ldap_scope) {
case 'base':
$schema_search = @ldap_read($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER);
break;
case 'one':
$schema_search = @ldap_list($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER);
break;
}
if (is_null($schema_search))
continue;
$schema_entries = @ldap_get_entries($this->connect($method),$schema_search);
if (DEBUG_ENABLED)
debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries);
if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) {
if (DEBUG_ENABLED)
debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)');
break;
}
if (DEBUG_ENABLED)
debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)');
unset($schema_entries);
$schema_search = null;
}
}
$schema_error_message = 'Please contact the phpLDAPadmin developers and let them know:<ul><li>Which LDAP server you are running, including which version<li>What OS it is running on<li>Which version of PHP<li>As well as a link to some documentation that describes how to obtain the SCHEMA information</ul><br />We\'ll then add support for your LDAP server in an upcoming release.';
$schema_error_message_array = array('objectclasses','attributetypes');
# Shall we just give up?
if (is_null($schema_search)) {
# We need to have objectclasses and attribues, so display an error, asking the user to get us this information.
if (in_array($schema_to_fetch,$schema_error_message_array))
system_message(array(
'title'=>sprintf('%s (%s)',_('Our attempts to find your SCHEMA have failed'),$schema_to_fetch),
'body'=>sprintf('<b>%s</b>: %s',_('Error'),$schema_error_message),
'type'=>'error'));
else
if (DEBUG_ENABLED)
debug_log('Returning because schema_search is NULL ()',25,0,__FILE__,__LINE__,__METHOD__);
# We'll set this, so if we return here our cache will return the known false.
$this->_schema_entries[$schema_to_fetch] = false;
return false;
}
if (! $schema_entries) {
$return = false;
if (DEBUG_ENABLED)
debug_log('Returning false since ldap_get_entries() returned false.',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
if ($olc_schema_found) {
unset ($schema_entries['count']);
foreach ($schema_entries as $entry) {
if (isset($entry[$olc_schema])) {
unset($entry[$olc_schema]['count']);
foreach ($entry[$olc_schema] as $schema_definition)
/* Schema definitions in child nodes prefix the schema entries with "{n}"
the preg_replace call strips out this prefix. */
$schema[] = preg_replace('/^\{\d*\}\(/','(',$schema_definition);
}
}
if (isset($schema)) {
$this->_schema_entries[$olc_schema] = $schema;
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema);
return $schema;
} else
return null;
}
if (! isset($schema_entries[0][$schema_to_fetch])) {
if (in_array($schema_to_fetch,$schema_error_message_array)) {
error(sprintf('Our attempts to find your SCHEMA for "%s" have return UNEXPECTED results.<br /><br /><small>(We expected a "%s" in the $schema array but it wasnt there.)</small><br /><br />%s<br /><br />Dump of $schema_search:<hr /><pre><small>%s</small></pre>',
$schema_to_fetch,gettype($schema_search),$schema_error_message,serialize($schema_entries)),'error','index.php');
} else {
$return = false;
if (DEBUG_ENABLED)
debug_log('Returning because (%s) isnt in the schema array. (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema_to_fetch,$return);
return $return;
}
}
/* Make a nice array of this form:
Array (
[0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...)"
[1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...)"
etc.) */
$schema = $schema_entries[0][$schema_to_fetch];
unset($schema['count']);
$this->_schema_entries[$schema_to_fetch] = $schema;
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema);
return $schema;
}
/**
* Gets a single ObjectClass object specified by name.
*
* @param string $oclass_name The name of the objectClass to fetch.
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return ObjectClass The specified ObjectClass object or false on error.
*
* @see ObjectClass
* @see SchemaObjectClasses
*/
public function getSchemaObjectClass($oclass_name,$method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
$oclass_name = strtolower($oclass_name);
$socs = $this->SchemaObjectClasses($method,$dn);
# Default return value
$return = false;
if (isset($socs[$oclass_name]))
$return = $socs[$oclass_name];
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Gets a single AttributeType object specified by name.
*
* @param string $oclass_name The name of the AttributeType to fetch.
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return AttributeType The specified AttributeType object or false on error.
*
* @see AttributeType
* @see SchemaAttributes
*/
public function getSchemaAttribute($attr_name,$method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
$attr_name = strtolower($attr_name);
$sattrs = $this->SchemaAttributes($method,$dn);
# Default return value
$return = false;
if (isset($sattrs[$attr_name]))
$return = $sattrs[$attr_name];
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Gets an associative array of ObjectClass objects for the specified
* server. Each array entry's key is the name of the objectClass
* in lower-case and the value is an ObjectClass object.
*
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return array An array of ObjectClass objects.
*
* @see ObjectClass
* @see getSchemaObjectClass
*/
public function SchemaObjectClasses($method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
# Set default return
$return = null;
if ($return = get_cached_item($this->index,'schema','objectclasses')) {
if (DEBUG_ENABLED)
debug_log('Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'objectclasses');
return $return;
}
$raw = $this->getRawSchema($method,'objectclasses',$dn);
if ($raw) {
# Build the array of objectClasses
$return = array();
foreach ($raw as $line) {
if (is_null($line) || ! strlen($line))
continue;
$object_class = new ObjectClass($line,$this);
$return[$object_class->getName()] = $object_class;
}
# Now go through and reference the parent/child relationships
foreach ($return as $oclass)
foreach ($oclass->getSupClasses() as $parent_name)
if (isset($return[strtolower($parent_name)]))
$return[strtolower($parent_name)]->addChildObjectClass($oclass->getName(false));
ksort($return);
# cache the schema to prevent multiple schema fetches from LDAP server
set_cached_item($this->index,'schema','objectclasses',$return);
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Gets an associative array of AttributeType objects for the specified
* server. Each array entry's key is the name of the attributeType
* in lower-case and the value is an AttributeType object.
*
* @param string $dn (optional) It is easier to fetch schema if a DN is provided
* which defines the subschemaSubEntry attribute (all entries should).
*
* @return array An array of AttributeType objects.
*/
public function SchemaAttributes($method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
# Set default return
$return = null;
if ($return = get_cached_item($this->index,'schema','attributes')) {
if (DEBUG_ENABLED)
debug_log('(): Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'attributes');
return $return;
}
$raw = $this->getRawSchema($method,'attributeTypes',$dn);
if ($raw) {
# build the array of attribueTypes
$syntaxes = $this->SchemaSyntaxes($method,$dn);
$attrs = array();
/**
* bug 856832: create two arrays - one indexed by name (the standard
* $attrs array above) and one indexed by oid (the new $attrs_oid array
* below). This will help for directory servers, like IBM's, that use OIDs
* in their attribute definitions of SUP, etc
*/
$attrs_oid = array();
foreach ($raw as $line) {
if (is_null($line) || ! strlen($line))
continue;
$attr = new AttributeType($line);
if (isset($syntaxes[$attr->getSyntaxOID()])) {
$syntax = $syntaxes[$attr->getSyntaxOID()];
$attr->setType($syntax->getDescription());
}
$attrs[$attr->getName()] = $attr;
/**
* bug 856832: create an entry in the $attrs_oid array too. This
* will be a ref to the $attrs entry for maintenance and performance
* reasons
*/
$attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()];
}
# go back and add data from aliased attributeTypes
foreach ($attrs as $name => $attr) {
$aliases = $attr->getAliases();
if (is_array($aliases) && count($aliases) > 0) {
/* foreach of the attribute's aliases, create a new entry in the attrs array
* with its name set to the alias name, and all other data copied.*/
foreach ($aliases as $alias_attr_name) {
$new_attr = clone $attr;
$new_attr->setName($alias_attr_name);
$new_attr->addAlias($attr->getName(false));
$new_attr->removeAlias($alias_attr_name);
$new_attr_key = strtolower($alias_attr_name);
$attrs[$new_attr_key] = $new_attr;
}
}
}
# go back and add any inherited descriptions from parent attributes (ie, cn inherits name)
foreach ($attrs as $key => $attr) {
$sup_attr_name = $attr->getSupAttribute();
$sup_attr = null;
if (trim($sup_attr_name)) {
/* This loop really should traverse infinite levels of inheritance (SUP) for attributeTypes,
* but just in case we get carried away, stop at 100. This shouldn't happen, but for
* some weird reason, we have had someone report that it has happened. Oh well.*/
$i = 0;
while ($i++<100 /** 100 == INFINITY ;) */) {
if (isset($attrs_oid[$sup_attr_name])) {
$attr->setSupAttribute($attrs_oid[$sup_attr_name]->getName());
$sup_attr_name = $attr->getSupAttribute();
}
if (! isset($attrs[strtolower($sup_attr_name)])){
error(sprintf('Schema error: attributeType "%s" inherits from "%s", but attributeType "%s" does not exist.',
$attr->getName(),$sup_attr_name,$sup_attr_name),'error','index.php');
return;
}
$sup_attr = $attrs[strtolower($sup_attr_name)];
$sup_attr_name = $sup_attr->getSupAttribute();
# Does this superior attributeType not have a superior attributeType?
if (is_null($sup_attr_name) || strlen(trim($sup_attr_name)) == 0) {
/* Since this attribute's superior attribute does not have another superior
* attribute, clone its properties for this attribute. Then, replace
* those cloned values with those that can be explicitly set by the child
* attribute attr). Save those few properties which the child can set here:*/
$tmp_name = $attr->getName(false);
$tmp_oid = $attr->getOID();
$tmp_sup = $attr->getSupAttribute();
$tmp_aliases = $attr->getAliases();
$tmp_single_val = $attr->getIsSingleValue();
$tmp_desc = $attr->getDescription();
/* clone the SUP attributeType and populate those values
* that were set by the child attributeType */
$attr = clone $sup_attr;
$attr->setOID($tmp_oid);
$attr->setName($tmp_name);
$attr->setSupAttribute($tmp_sup);
$attr->setAliases($tmp_aliases);
$attr->setDescription($tmp_desc);
/* only overwrite the SINGLE-VALUE property if the child explicitly sets it
* (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */
if ($tmp_single_val)
$attr->setIsSingleValue(true);
/* replace this attribute in the attrs array now that we have populated
new values therein */
$attrs[$key] = $attr;
# very important: break out after we are done with this attribute
$sup_attr_name = null;
$sup_attr = null;
break;
}
}
}
}
ksort($attrs);
# Add the used in and required_by values.
$socs = $this->SchemaObjectClasses($method);
if (! is_array($socs))
return array();
foreach ($socs as $object_class) {
$must_attrs = $object_class->getMustAttrNames();
$may_attrs = $object_class->getMayAttrNames();
$oclass_attrs = array_unique(array_merge($must_attrs,$may_attrs));
# Add Used In.
foreach ($oclass_attrs as $attr_name)
if (isset($attrs[strtolower($attr_name)]))
$attrs[strtolower($attr_name)]->addUsedInObjectClass($object_class->getName(false));
# Add Required By.
foreach ($must_attrs as $attr_name)
if (isset($attrs[strtolower($attr_name)]))
$attrs[strtolower($attr_name)]->addRequiredByObjectClass($object_class->getName(false));
# Force May
foreach ($object_class->getForceMayAttrs() as $attr_name)
if (isset($attrs[strtolower($attr_name->name)]))
$attrs[strtolower($attr_name->name)]->setForceMay();
}
$return = $attrs;
# cache the schema to prevent multiple schema fetches from LDAP server
set_cached_item($this->index,'schema','attributes',$return);
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Returns an array of MatchingRule objects for the specified server.
* The key of each entry is the OID of the matching rule.
*/
public function MatchingRules($method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
# Set default return
$return = null;
if ($return = get_cached_item($this->index,'schema','matchingrules')) {
if (DEBUG_ENABLED)
debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'matchingrules');
return $return;
}
# build the array of MatchingRule objects
$raw = $this->getRawSchema($method,'matchingRules',$dn);
if ($raw) {
$rules = array();
foreach ($raw as $line) {
if (is_null($line) || ! strlen($line))
continue;
$rule = new MatchingRule($line);
$key = $rule->getName();
$rules[$key] = $rule;
}
ksort($rules);
/* For each MatchingRuleUse entry, add the attributes who use it to the
* MatchingRule in the $rules array.*/
$raw = $this->getRawSchema($method,'matchingRuleUse');
if ($raw != false) {
foreach ($raw as $line) {
if (is_null($line) || ! strlen($line))
continue;
$rule_use = new MatchingRuleUse($line);
$key = $rule_use->getName();
if (isset($rules[$key]))
$rules[$key]->setUsedByAttrs($rule_use->getUsedByAttrs());
}
} else {
/* No MatchingRuleUse entry in the subschema, so brute-forcing
* the reverse-map for the "$rule->getUsedByAttrs()" data.*/
$sattrs = $this->SchemaAttributes($method,$dn);
if (is_array($sattrs))
foreach ($sattrs as $attr) {
$rule_key = strtolower($attr->getEquality());
if (isset($rules[$rule_key]))
$rules[$rule_key]->addUsedByAttr($attr->getName(false));
}
}
$return = $rules;
# cache the schema to prevent multiple schema fetches from LDAP server
set_cached_item($this->index,'schema','matchingrules',$return);
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* Returns an array of Syntax objects that this LDAP server uses mapped to
* their descriptions. The key of each entry is the OID of the Syntax.
*/
public function SchemaSyntaxes($method=null,$dn='') {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs);
# Set default return
$return = null;
if ($return = get_cached_item($this->index,'schema','syntaxes')) {
if (DEBUG_ENABLED)
debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'syntaxes');
return $return;
}
$raw = $this->getRawSchema($method,'ldapSyntaxes',$dn);
if ($raw) {
# build the array of attributes
$return = array();
foreach ($raw as $line) {
if (is_null($line) || ! strlen($line))
continue;
$syntax = new Syntax($line);
$key = strtolower(trim($syntax->getOID()));
if (! $key)
continue;
$return[$key] = $syntax;
}
ksort($return);
# cache the schema to prevent multiple schema fetches from LDAP server
set_cached_item($this->index,'schema','syntaxes',$return);
}
if (DEBUG_ENABLED)
debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return);
return $return;
}
/**
* This function determines if the specified attribute is contained in the force_may list
* as configured in config.php.
*
* @return boolean True if the specified attribute is configured to be force as a may attribute
*/
function isForceMay($attr_name) {
if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS'))
debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs);
return in_array($attr_name,unserialize(strtolower(serialize($this->getValue('server','force_may')))));
}
/**
* Much like getDNAttrValues(), but only returns the values for