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;
 | 
						|
  }
 | 
						|
}
 |