248 lines
8.2 KiB
PHP
248 lines
8.2 KiB
PHP
<?php
|
|
namespace nur\ldap\schemas;
|
|
|
|
use nur\A;
|
|
use nur\b\IllegalAccessException;
|
|
use nur\b\ValueException;
|
|
use nur\data\types\md_utils;
|
|
use nur\data\types\Metadata;
|
|
use nur\ldap\consts;
|
|
use nur\ldap\LdapAttr;
|
|
use nur\ldap\LdapConn;
|
|
use nur\ldap\syntaxes\BinarySyntax;
|
|
use nur\ldap\syntaxes\StringSyntax;
|
|
|
|
/**
|
|
* Class LdapSchemaExtractor: extracteur de schéma LDAP, pour utilisation avec
|
|
* PHP
|
|
*/
|
|
class LdapSchemaExtractor {
|
|
function __construct(?array $schemaInfos=null) {
|
|
if ($schemaInfos !== null) {
|
|
[
|
|
"ldap_syntaxes" => $this->ldapSyntaxes,
|
|
"attribute_types" => $this->attributeTypes,
|
|
"object_classes" => $this->objectClasses,
|
|
] = $schemaInfos;
|
|
}
|
|
}
|
|
|
|
protected $ldapSyntaxes;
|
|
|
|
protected $attributeTypes;
|
|
|
|
protected $objectClasses;
|
|
|
|
function loadSchema(LdapConn $conn): array {
|
|
$schema = null;
|
|
$schemaDn = $conn->getRootDse()->first("subschemaSubentry");
|
|
if ($schemaDn !== null) {
|
|
$schema = $conn->empty()->load($schemaDn, $conn->_search($schemaDn, [
|
|
"suffix" => "",
|
|
"attrs" => [
|
|
"ldapSyntaxes",
|
|
"attributeTypes",
|
|
"objectClasses",
|
|
],
|
|
"scope" => "base",
|
|
])->first());
|
|
}
|
|
if ($schema === null) {
|
|
throw new IllegalAccessException("unable to find subschemaSubentry attribute");
|
|
}
|
|
|
|
$parser = new LseSyntax();
|
|
$ldapSyntaxes = [];
|
|
foreach ($schema->get("ldapSyntaxes", []) as $ldapSyntax) {
|
|
$ldapSyntax = $parser->parse($ldapSyntax);
|
|
$ldapSyntaxes[$ldapSyntax["oid"]] = $ldapSyntax;
|
|
}
|
|
$parser = new LseAttribute();
|
|
$attributeTypes = [];
|
|
foreach ($schema->get("attributeTypes", []) as $attributeType) {
|
|
$attributeType = $parser->parse($attributeType);
|
|
$attributeTypes[$attributeType["oid"]] = $attributeType;
|
|
}
|
|
$parser = new LseObjectClass();
|
|
$objectClasses = [];
|
|
foreach ($schema->get("objectClasses", []) as $objectClass) {
|
|
$objectClass = $parser->parse($objectClass);
|
|
$objectClasses[$objectClass["oid"]] = $objectClass;
|
|
}
|
|
return [
|
|
"ldap_syntaxes" => $this->ldapSyntaxes = $ldapSyntaxes,
|
|
"attribute_types" => $this->attributeTypes = $attributeTypes,
|
|
"object_classes" => $this->objectClasses = $objectClasses,
|
|
];
|
|
}
|
|
|
|
protected $syntaxes;
|
|
protected $attributes;
|
|
protected $canonAttrs;
|
|
protected $classes;
|
|
protected $canonClasses;
|
|
|
|
function init(): array {
|
|
## calculer la liste des syntaxes, et les classer par OID
|
|
$ldapSyntaxes = $this->ldapSyntaxes;
|
|
# rajouter une liste connue de syntaxes
|
|
A::merge($ldapSyntaxes, consts::KNOWN_SLAPD_SYNTAXES);
|
|
$syntaxes = [];
|
|
foreach ($ldapSyntaxes as $syntax) {
|
|
$oid = $syntax["oid"];
|
|
# si la syntaxe a déjà été définie, ignorer
|
|
if (array_key_exists($oid, $syntaxes)) continue;
|
|
$class = A::get(consts::KNOWN_SYNTAX_CLASSES, $oid);
|
|
if ($class === null) {
|
|
$binary = $syntax["x_not_human_readable"] || $syntax["x_binary_transfer_required"];
|
|
$class = $binary? BinarySyntax::class: StringSyntax::class;
|
|
}
|
|
$syntax["class"] = $class;
|
|
$syntaxes[$oid] = $syntax;
|
|
}
|
|
|
|
## calculer la liste des attributs, et les classer par nom canonique
|
|
$attributes = [];
|
|
$canonAttrs = [];
|
|
foreach ($this->attributeTypes as $attribute) {
|
|
$names = $attribute["names"];
|
|
$canonName = $names[0];
|
|
$attribute["name"] = $canonName;
|
|
foreach ($names as $name) {
|
|
$canonAttrs[strtolower($name)] = $canonName;
|
|
}
|
|
$attribute["class"] = A::_pget($syntaxes, [$attribute["syntax"], "class"]);
|
|
$attributes[strtolower($canonName)] = $attribute;
|
|
}
|
|
# résoudre l'héritage des attributs
|
|
foreach ($attributes as &$attribute) {
|
|
foreach ($attribute["sups"] as $sup) {
|
|
$sup = strtolower(A::get($canonAttrs, strtolower($sup), $sup));
|
|
A::update_n($attribute, $attributes[$sup]);
|
|
}
|
|
}; unset($attribute);
|
|
# puis mettre à false les valeurs booléennes nulles
|
|
foreach ($attributes as &$attribute) {
|
|
foreach (LseAttribute::BOOL_ATTRS as $name) {
|
|
$attribute[$name] = boolval($attribute[$name]);
|
|
}
|
|
}; unset($attribute);
|
|
|
|
## calculer la liste des classes, et les classer par nom canonique.
|
|
## les noms des attributs sont aussi canonisés
|
|
$classes = [];
|
|
$canonClasses = [];
|
|
foreach ($this->objectClasses as $class) {
|
|
$names = $class["names"];
|
|
$canonName = $names[0];
|
|
$class["name"] = $canonName;
|
|
foreach ($names as $name) {
|
|
$canonClasses[strtolower($name)] = $canonName;
|
|
}
|
|
$musts = A::with($class["musts"]);
|
|
foreach ($musts as &$name) {
|
|
$name = A::get($canonAttrs, strtolower($name), $name);
|
|
}; unset($name);
|
|
$class["musts"] = $musts;
|
|
$mays = A::with($class["mays"]);
|
|
foreach ($mays as &$name) {
|
|
$name = A::get($canonAttrs, strtolower($name), $name);
|
|
}; unset($name);
|
|
$class["mays"] = $mays;
|
|
$class["attrs"] = array_merge($musts, $mays);
|
|
$classes[strtolower($canonName)] = $class;
|
|
}
|
|
# résoudre l'héritage des classes
|
|
foreach ($classes as &$class) {
|
|
foreach ($class["sups"] as $sup) {
|
|
$sup = strtolower(A::get($canonAttrs, strtolower($sup), $sup));
|
|
$sup = $classes[$sup];
|
|
A::update_n($class, $sup);
|
|
A::merge($class["musts"], $sup["musts"]);
|
|
A::merge($class["mays"], $sup["mays"]);
|
|
}
|
|
}; unset($class);
|
|
|
|
## fin de l'initialisation
|
|
return [
|
|
"syntaxes" => $this->syntaxes = $syntaxes,
|
|
"attributes" => $this->attributes = $attributes,
|
|
"canon_attrs" => $this->canonAttrs = $canonAttrs,
|
|
"classes" => $this->classes = $classes,
|
|
"canon_classes" => $this->canonClasses = $canonClasses,
|
|
];
|
|
}
|
|
|
|
const getAttributes_overrides_SCHEMA = [
|
|
"name" => "string",
|
|
"class" => "?string",
|
|
"set" => "?int",
|
|
"reset" => "?int",
|
|
];
|
|
/** @var Metadata */
|
|
private static $getAttributes_overrides_md;
|
|
|
|
function getAttributes(array $objectClasses, ?array $overrides=null): array {
|
|
if ($overrides !== null) {
|
|
$tmp = [];
|
|
foreach ($overrides as $name => $override) {
|
|
$attribute = ValueException::check_nn(
|
|
A::get($this->attributes, strtolower($name))
|
|
, "$name: attribut non défini");
|
|
$tmp[$attribute["name"]] = $override;
|
|
}
|
|
$overrides = $tmp;
|
|
$md = md_utils::ensure_md(self::$getAttributes_overrides_md, self::getAttributes_overrides_SCHEMA);
|
|
$md->eachEnsureSchema($overrides);
|
|
}
|
|
|
|
$nameRequired = [];
|
|
foreach ($objectClasses as $name) {
|
|
$name = A::get($this->canonClasses, strtolower($name), $name);
|
|
$class = ValueException::check_nn(
|
|
A::get($this->classes, strtolower($name))
|
|
, "$name: classe non définie");
|
|
foreach ($class["musts"] as $must) {
|
|
$nameRequired[$must] = true;
|
|
}
|
|
foreach ($class["mays"] as $may) {
|
|
A::replace_nx($nameRequired, $may, false);
|
|
}
|
|
}
|
|
$attributes = [
|
|
"dn" => [
|
|
"name" => "dn",
|
|
"class" => StringSyntax::class,
|
|
"flags" => LdapAttr::MONOVALUED,
|
|
],
|
|
];
|
|
foreach ($nameRequired as $name => $required) {
|
|
$lname = strtolower($name);
|
|
$attribute = ValueException::check_nn(
|
|
A::get($this->attributes, $lname)
|
|
, "$name: attribut non défini");
|
|
$syntax = ValueException::check_nn(
|
|
A::get($this->syntaxes, $attribute["syntax"])
|
|
, "$attribute[syntax]: syntaxe non définie");
|
|
$class = $attribute["class"];
|
|
$monovalued = $attribute["single_value"]? LdapAttr::MONOVALUED: 0;
|
|
$binary = $syntax["x_binary_transfer_required"]? LdapAttr::BINARY: 0;
|
|
$ordered = $attribute["x_ordered"]? LdapAttr::ORDERED: 0;
|
|
$notHumanReadable = $syntax["x_not_human_readable"]? LdapAttr::NOT_HUMAN_READABLE: 0;
|
|
$flags = $monovalued + $binary + $ordered + $notHumanReadable;
|
|
$override = A::get($overrides, $name);
|
|
if ($override !== null) {
|
|
if ($override["class"] !== null) $class = $override["class"];
|
|
if ($override["set"] !== null) $flags = $flags | $override["set"];
|
|
if ($override["reset"] !== null) $flags = $flags & ~$override["reset"];
|
|
}
|
|
$attributes[$lname] = [
|
|
"name" => $name,
|
|
"class" => $class,
|
|
"flags" => $flags,
|
|
];
|
|
}
|
|
return $attributes;
|
|
}
|
|
}
|