nur-sery/nur_src/ldap/schemas/LdapSchemaExtractor.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;
}
}