php-eesyldap/src/Schema.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;
}
}