243 lines
6.6 KiB
PHP
243 lines
6.6 KiB
PHP
<?php
|
|
namespace EesyLDAP;
|
|
|
|
|
|
/**
|
|
* @property-read bool $loaded
|
|
*/
|
|
class Schema {
|
|
|
|
/**
|
|
* CN=SubSchema Entry object
|
|
* @var Entry
|
|
*/
|
|
protected $entry;
|
|
|
|
/**
|
|
* LDAP connection
|
|
* @var Ldap
|
|
*/
|
|
protected $ldap;
|
|
|
|
/**
|
|
* Some syntax OID constants
|
|
*/
|
|
public const SYNTAX_BOOLEAN = '1.3.6.1.4.1.1466.115.121.1.7';
|
|
public const SYNTAX_DIRECTORY_STRING = '1.3.6.1.4.1.1466.115.121.1.15';
|
|
public const SYNTAX_DISTINGUISHED_NAME = '1.3.6.1.4.1.1466.115.121.1.12';
|
|
public const SYNTAX_INTEGER = '1.3.6.1.4.1.1466.115.121.1.27';
|
|
public const SYNTAX_JPEG = '1.3.6.1.4.1.1466.115.121.1.28';
|
|
public const SYNTAX_NUMERIC_STRING = '1.3.6.1.4.1.1466.115.121.1.36';
|
|
public const SYNTAX_OID = '1.3.6.1.4.1.1466.115.121.1.38';
|
|
public const SYNTAX_OCTET_STRING = '1.3.6.1.4.1.1466.115.121.1.40';
|
|
|
|
/**
|
|
* Map SubSchema entry attributes to schema entry type
|
|
*
|
|
* @var array<string,string>
|
|
*/
|
|
protected static $attributes_to_entry_types = array(
|
|
'attributeTypes' => 'Attribute',
|
|
'matchingRules' => 'MatchingRule',
|
|
'matchingRuleUse' => 'MatchingRuleUse',
|
|
'objectClasses' => 'ObjectClass',
|
|
'ldapSyntaxes' => 'Syntax',
|
|
);
|
|
|
|
/**
|
|
* Loaded schema entries ordered by type
|
|
* @var array<string,array<string,Schema\SchemaEntry>>
|
|
*/
|
|
protected $entries = array();
|
|
|
|
/**
|
|
* Loaded schema entries mapped with their OID
|
|
* @var array<string,Schema\SchemaEntry>
|
|
*/
|
|
protected $oids = array();
|
|
|
|
/**
|
|
* Loaded telltale
|
|
* @var bool
|
|
*/
|
|
protected $loaded = false;
|
|
|
|
/**
|
|
* Constructor
|
|
* @param Ldap $ldap The LDAP connection
|
|
* @param Entry $entry The cn=SubSchema LDAP entry object
|
|
* @return void
|
|
*/
|
|
public function __construct(&$ldap, &$entry) {
|
|
$this -> ldap = $ldap;
|
|
$this -> entry = $entry;
|
|
$this -> parse();
|
|
$this -> loaded = true;
|
|
}
|
|
|
|
/**
|
|
* Magic method to get schema key
|
|
* @param string $key
|
|
* @return mixed
|
|
* @throws \EesyLDAP\InvalidPropertyException
|
|
*/
|
|
public function __get($key) {
|
|
switch ($key) {
|
|
case 'loaded':
|
|
return $this -> loaded;
|
|
}
|
|
throw new \EesyLDAP\InvalidPropertyException(
|
|
"Invalid property '$key' requested on '".get_called_class()."'"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Load schema from provided LDAP connection
|
|
* @param Ldap $ldap
|
|
* @param bool|null $raise
|
|
* @return Schema|false Schema object on success, false on error
|
|
*/
|
|
public static function load(&$ldap, $raise=null) {
|
|
$entry = $ldap->get_entry(
|
|
'cn=SubSchema', '(objectclass=*)',
|
|
array_keys(self :: $attributes_to_entry_types)
|
|
);
|
|
if (!$entry instanceof Entry)
|
|
return $ldap->error('Fail to load cn=SubSchema entry', $raise);
|
|
return new Schema($ldap, $entry);
|
|
}
|
|
|
|
/**
|
|
* Parse SubSchema entry attributes values
|
|
* @return void
|
|
*/
|
|
protected function parse() {
|
|
foreach (self :: $attributes_to_entry_types as $attr => $type) {
|
|
$type_name = strtolower($type);
|
|
foreach($this -> entry -> get_raw_values($attr, array(), true) as $value) {
|
|
// @phpstan-ignore-next-line
|
|
$entry = call_user_func("\\EesyLDAP\\Schema\\$type::parse", $value);
|
|
if (!$entry instanceof Schema\SchemaEntry) {
|
|
$this -> ldap -> error("Fail to parse %s schema value: %s", null, $attr, $value);
|
|
continue;
|
|
}
|
|
$oid = $entry->oid;
|
|
if ($type != 'MatchingRuleUse') {
|
|
if (array_key_exists($oid, $this->oids)) {
|
|
$this -> ldap -> error(
|
|
"Duplicate OID %s found in schema: %s / %s", null, $oid, $this->oids[$oid], $entry);
|
|
continue;
|
|
}
|
|
$this->oids[$oid] = $entry;
|
|
}
|
|
|
|
if (!array_key_exists($type_name, $this -> entries))
|
|
$this -> entries[$type_name] = array();
|
|
$name = $entry->name;
|
|
if (array_key_exists($name, $this -> entries[$type_name])) {
|
|
$this -> ldap -> error(
|
|
"Duplicate %s schema entry %s found: %s / %s",
|
|
null, $type, $name, $this -> entries[$type_name][$name], $entry
|
|
);
|
|
continue;
|
|
}
|
|
$this -> entries[$type_name][$name] = $entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get a schema entry by type and name/oid
|
|
* @param string $type
|
|
* @param string $name_or_oid
|
|
* @return Schema\SchemaEntry|false
|
|
*/
|
|
protected function _get_entry($type, $name_or_oid) {
|
|
if (
|
|
array_key_exists($name_or_oid, $this -> oids)
|
|
&& is_a($this -> oids[$name_or_oid], "\\EesyLDAP\\Schema\\$type")
|
|
)
|
|
return $this -> oids[$name_or_oid];
|
|
$type_name = strtolower($type);
|
|
if (!array_key_exists($type_name, $this -> entries))
|
|
return false;
|
|
foreach($this -> entries[$type_name] as $attr)
|
|
if ($attr->is_me($name_or_oid))
|
|
return $attr;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get an attribute by name or oid
|
|
* @param string $name_or_oid
|
|
* @return Schema\Attribute|false
|
|
*/
|
|
public function attribute($name_or_oid) {
|
|
// @phpstan-ignore-next-line
|
|
return $this -> _get_entry('Attribute', $name_or_oid);
|
|
}
|
|
|
|
/**
|
|
* Get an objectclass by name or oid
|
|
* @param string $name_or_oid
|
|
* @return \EesyLDAP\Schema\ObjectClass|false
|
|
*/
|
|
public function objectclass($name_or_oid) {
|
|
// @phpstan-ignore-next-line
|
|
return $this -> _get_entry('ObjectClass', $name_or_oid);
|
|
}
|
|
|
|
/**
|
|
* Get a matching rule by name or oid
|
|
* @param string $name_or_oid
|
|
* @return Schema\MatchingRule|false
|
|
*/
|
|
public function matching_rule($name_or_oid) {
|
|
// @phpstan-ignore-next-line
|
|
return $this -> _get_entry('MatchingRule', $name_or_oid);
|
|
}
|
|
|
|
/**
|
|
* Get a matching rule use by name or oid
|
|
* @param string $name_or_oid
|
|
* @return Schema\MatchingRuleUse|false
|
|
*/
|
|
public function matching_rule_use($name_or_oid) {
|
|
// @phpstan-ignore-next-line
|
|
return $this -> _get_entry('MatchingRuleUse', $name_or_oid);
|
|
}
|
|
|
|
/**
|
|
* Get a syntax by name or oid
|
|
* @param string $name_or_oid
|
|
* @return Schema\Syntax|false
|
|
*/
|
|
public function syntax($name_or_oid) {
|
|
// @phpstan-ignore-next-line
|
|
return $this -> _get_entry('Syntax', $name_or_oid);
|
|
}
|
|
|
|
/**
|
|
* Check if given objectclass have the specified attribute
|
|
* @param string $attr The attribute name
|
|
* @param string|array<string> $objectclasses List of objectclass
|
|
* @return bool
|
|
*/
|
|
public function has_attribute($attr, ...$objectclasses) {
|
|
foreach($objectclasses as $objectclass) {
|
|
if (is_array($objectclass)) {
|
|
if (call_user_func_array(array($this, 'has_attribute'), array_merge(array($attr), $objectclass)))
|
|
return true;
|
|
continue;
|
|
}
|
|
$oc = $this -> objectclass($objectclass);
|
|
if (!$oc)
|
|
continue;
|
|
if ($oc->has_attribute($attr))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
}
|