diff --git a/app/Classes/LDAP/Schema/ObjectClass.php b/app/Classes/LDAP/Schema/ObjectClass.php index 68a7cda..84b3f0e 100644 --- a/app/Classes/LDAP/Schema/ObjectClass.php +++ b/app/Classes/LDAP/Schema/ObjectClass.php @@ -4,7 +4,6 @@ namespace App\Classes\LDAP\Schema; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; -use LdapRecord\Connection; use App\Classes\LDAP\Server; use App\Exceptions\InvalidUsage; @@ -19,7 +18,6 @@ use App\Ldap\Entry; class ObjectClass extends Base { // The server ID that this objectclass belongs to. private Server $server; - private Connection $connection; // Array of objectClass names from which this objectClass inherits private Collection $sup_classes; @@ -51,7 +49,7 @@ class ObjectClass extends Base { * * eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass ) */ - public function __construct(string $line,Entry $entry,Server $server) + public function __construct(string $line,Server $server) { parent::__construct($line); @@ -60,7 +58,6 @@ class ObjectClass extends Base { $strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE); // Init - $this->connection = $entry->getConnection(); $this->server = $server; $this->may_attrs = collect(); $this->may_force = collect(); diff --git a/app/Classes/LDAP/Server.php b/app/Classes/LDAP/Server.php index 6b64af4..7a04ea8 100644 --- a/app/Classes/LDAP/Server.php +++ b/app/Classes/LDAP/Server.php @@ -32,6 +32,19 @@ class Server 'matchingrules', ]; + public function __get(string $key): mixed + { + switch ($key) { + case 'attributetypes': return $this->attributetypes; + case 'ldapsyntaxes': return $this->ldapsyntaxes; + case 'matchingrules': return $this->matchingrules; + case 'objectclasses': return $this->objectclasses; + + default: + throw new Exception('Unknown key:'.$key); + } + } + /** * Query the server for a DN and return its children and if those children have children. * @@ -151,230 +164,232 @@ class Server if (! in_array($item,self::schema_types)) throw new InvalidUsage('Invalid request to fetch schema: '.$item); - // First pass if we have already retrieved the schema item - switch ($item) { - case 'attributetypes': - if (isset($this->attributetypes)) - return is_null($key) ? $this->attributetypes : $this->attributetypes->get($key); - else - $this->attributetypes = collect(); + $result = Cache::remember('schema'.$item,config('ldap.cache.time'),function() use ($item) { + // First pass if we have already retrieved the schema item + switch ($item) { + case 'attributetypes': + if (isset($this->attributetypes)) + return $this->attributetypes; + else + $this->attributetypes = collect(); - break; + break; - case 'ldapsyntaxes': - if (isset($this->ldapsyntaxes)) - return is_null($key) ? $this->ldapsyntaxes : $this->ldapsyntaxes->get($key); - else - $this->ldapsyntaxes = collect(); + case 'ldapsyntaxes': + if (isset($this->ldapsyntaxes)) + return $this->ldapsyntaxes; + else + $this->ldapsyntaxes = collect(); - break; + break; - case 'matchingrules': - if (isset($this->matchingrules)) - return is_null($key) ? $this->matchingrules : $this->matchingrules->get($key); - else - $this->matchingrules = collect(); + case 'matchingrules': + if (isset($this->matchingrules)) + return $this->matchingrules; + else + $this->matchingrules = collect(); - break; - - /* - case 'matchingruleuse': - if (isset($this->matchingruleuse)) - return is_null($key) ? $this->matchingruleuse : $this->matchingruleuse->get($key); - else - $this->matchingruleuse = collect(); - - break; - */ - - case 'objectclasses': - if (isset($this->objectclasses)) - return is_null($key) ? $this->objectclasses : $this->objectclasses->get($key); - else - $this->objectclasses = collect(); - - break; - - // Shouldnt get here - default: - throw new InvalidUsage('Invalid request to fetch schema: '.$item); - } - - // Try to get the schema DN from the specified entry. - $schema_dn = Entry::schemaDN(); - $schema = (new Server)->fetch($schema_dn); - - switch ($item) { - case 'attributetypes': - Log::debug('Attribute Types'); - // build the array of attribueTypes - //$syntaxes = $this->SchemaSyntaxes($dn); - - foreach ($schema->{$item} as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $o = new AttributeType($line); - $this->attributetypes->put($o->name_lc,$o); - - /* - if (isset($syntaxes[$attr->getSyntaxOID()])) { - $syntax = $syntaxes[$attr->getSyntaxOID()]; - $attr->setType($syntax->getDescription()); - } - $this->attributetypes[$attr->getName()] = $attr; - */ - - /** - * bug 856832: create an entry in the $attrs_oid array too. This - * will be a ref to the $attrs entry for maintenance and performance - * reasons - */ - //$attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()]; - } - - // go back and add data from aliased attributeTypes - foreach ($this->attributetypes as $o) { - /* foreach of the attribute's aliases, create a new entry in the attrs array - * with its name set to the alias name, and all other data copied.*/ - - if ($o->aliases->count()) { - Log::debug(sprintf('\ Attribute [%s] has the following aliases [%s]',$o->name,$o->aliases->join(','))); - - foreach ($o->aliases as $alias) { - $new_attr = clone $o; - $new_attr->setName($alias); - $new_attr->addAlias($o->name); - $new_attr->removeAlias($alias); - - $this->attributetypes->put(strtolower($alias),$new_attr); - } - } - } - - // Now go through and reference the parent/child relationships - foreach ($this->attributetypes as $o) - if ($o->sup_attribute) { - $parent = strtolower($o->sup_attribute); - - if ($this->attributetypes->has($parent) !== FALSE) - $this->attributetypes[$parent]->addChild($o->name); - } - - // go through any children and add details if the child doesnt have them (ie, cn inherits name) - // @todo This doesnt traverse children properly, so children of children may not get the settings they should - foreach ($this->attributetypes as $parent) { - foreach ($parent->children as $child) { - $child = strtolower($child); - - /* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt - * (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */ - if (! is_null($parent->is_single_value) && is_null($this->attributetypes[$child]->is_single_value)) - $this->attributetypes[$child]->setIsSingleValue($parent->is_single_value); - } - } - - // Add the used in and required_by values. - foreach ($this->schema('objectclasses') as $object_class) { - $must_attrs = $object_class->getMustAttrNames(); - $may_attrs = $object_class->getMayAttrNames(); - $oclass_attrs = $must_attrs->merge($may_attrs)->unique(); - - // Add Used In. - foreach ($oclass_attrs as $attr_name) - if ($this->attributetypes->has(strtolower($attr_name))) - $this->attributetypes[strtolower($attr_name)]->addUsedInObjectClass($object_class->name); - - // Add Required By. - foreach ($must_attrs as $attr_name) - if ($this->attributetypes->has(strtolower($attr_name))) - $this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name); - - // Force May - foreach ($object_class->getForceMayAttrs() as $attr_name) - if ($this->attributetypes->has(strtolower($attr_name->name))) - $this->attributetypes[strtolower($attr_name->name)]->setForceMay(); - } - - return is_null($key) ? $this->attributetypes : $this->attributetypes->get($key); - - case 'objectclasses': - Log::debug('Object Classes'); - - foreach ($schema->{$item} as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $o = new ObjectClass($line,$schema,$this); - $this->objectclasses->put($o->name_lc,$o); - } - - // Now go through and reference the parent/child relationships - foreach ($this->objectclasses as $o) - foreach ($o->getSupClasses() as $parent) { - $parent = strtolower($parent); - if ($this->objectclasses->has($parent) !== FALSE) - $this->objectclasses[$parent]->addChildObjectClass($o->name); - } - - return is_null($key) ? $this->objectclasses : $this->objectclasses->get($key); - - case 'ldapsyntaxes': - Log::debug('LDAP Syntaxes'); - - foreach ($schema->{$item} as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $o = new LDAPSyntax($line); - $this->ldapsyntaxes->put(strtolower($o->oid),$o); - } - - return is_null($key) ? $this->ldapsyntaxes : $this->ldapsyntaxes->get($key); - - case 'matchingrules': - Log::debug('Matching Rules'); - $this->matchingruleuse = collect(); - - foreach ($schema->{$item} as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $o = new MatchingRule($line); - $this->matchingrules->put($o->name_lc,$o); - } + break; /* - * For each MatchingRuleUse entry, add the attributes who use it to the - * MatchingRule in the $rules array. - */ - if ($schema->matchingruleuse) { - foreach ($schema->matchingruleuse as $line) { + case 'matchingruleuse': + if (isset($this->matchingruleuse)) + return is_null($key) ? $this->matchingruleuse : $this->matchingruleuse->get($key); + else + $this->matchingruleuse = collect(); + + break; + */ + + case 'objectclasses': + if (isset($this->objectclasses)) + return $this->objectclasses; + else + $this->objectclasses = collect(); + + break; + + // Shouldnt get here + default: + throw new InvalidUsage('Invalid request to fetch schema: '.$item); + } + + // Try to get the schema DN from the specified entry. + $schema_dn = Entry::schemaDN(); + $schema = (new Server)->fetch($schema_dn); + + switch ($item) { + case 'attributetypes': + Log::debug('Attribute Types'); + // build the array of attribueTypes + //$syntaxes = $this->SchemaSyntaxes($dn); + + foreach ($schema->{$item} as $line) { if (is_null($line) || ! strlen($line)) continue; - $o = new MatchingRuleUse($line); - $this->matchingruleuse->put($o->name_lc,$o); + $o = new AttributeType($line); + $this->attributetypes->put($o->name_lc,$o); - if ($this->matchingrules->has($o->name_lc) !== FALSE) - $this->matchingrules[$o->name_lc]->setUsedByAttrs($o->getUsedByAttrs()); + /* + if (isset($syntaxes[$attr->getSyntaxOID()])) { + $syntax = $syntaxes[$attr->getSyntaxOID()]; + $attr->setType($syntax->getDescription()); + } + $this->attributetypes[$attr->getName()] = $attr; + */ + + /** + * bug 856832: create an entry in the $attrs_oid array too. This + * will be a ref to the $attrs entry for maintenance and performance + * reasons + */ + //$attrs_oid[$attr->getOID()] = &$attrs[$attr->getName()]; } - } else { - /* No MatchingRuleUse entry in the subschema, so brute-forcing - * the reverse-map for the "$rule->getUsedByAttrs()" data.*/ - foreach ($this->schema('attributetypes') as $attr) { - $rule_key = strtolower($attr->getEquality()); + // go back and add data from aliased attributeTypes + foreach ($this->attributetypes as $o) { + /* foreach of the attribute's aliases, create a new entry in the attrs array + * with its name set to the alias name, and all other data copied.*/ - if ($this->matchingrules->has($rule_key) !== FALSE) - $this->matchingrules[$rule_key]->addUsedByAttr($attr->name); + if ($o->aliases->count()) { + Log::debug(sprintf('\ Attribute [%s] has the following aliases [%s]',$o->name,$o->aliases->join(','))); + + foreach ($o->aliases as $alias) { + $new_attr = clone $o; + $new_attr->setName($alias); + $new_attr->addAlias($o->name); + $new_attr->removeAlias($alias); + + $this->attributetypes->put(strtolower($alias),$new_attr); + } + } } - } - return is_null($key) ? $this->matchingrules : $this->matchingrules->get($key); - } + // Now go through and reference the parent/child relationships + foreach ($this->attributetypes as $o) + if ($o->sup_attribute) { + $parent = strtolower($o->sup_attribute); - return NULL; + if ($this->attributetypes->has($parent) !== FALSE) + $this->attributetypes[$parent]->addChild($o->name); + } + + // go through any children and add details if the child doesnt have them (ie, cn inherits name) + // @todo This doesnt traverse children properly, so children of children may not get the settings they should + foreach ($this->attributetypes as $parent) { + foreach ($parent->children as $child) { + $child = strtolower($child); + + /* only overwrite the child's SINGLE-VALUE property if the parent has it set, and the child doesnt + * (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */ + if (! is_null($parent->is_single_value) && is_null($this->attributetypes[$child]->is_single_value)) + $this->attributetypes[$child]->setIsSingleValue($parent->is_single_value); + } + } + + // Add the used in and required_by values. + foreach ($this->schema('objectclasses') as $object_class) { + $must_attrs = $object_class->getMustAttrNames(); + $may_attrs = $object_class->getMayAttrNames(); + $oclass_attrs = $must_attrs->merge($may_attrs)->unique(); + + // Add Used In. + foreach ($oclass_attrs as $attr_name) + if ($this->attributetypes->has(strtolower($attr_name))) + $this->attributetypes[strtolower($attr_name)]->addUsedInObjectClass($object_class->name); + + // Add Required By. + foreach ($must_attrs as $attr_name) + if ($this->attributetypes->has(strtolower($attr_name))) + $this->attributetypes[strtolower($attr_name)]->addRequiredByObjectClass($object_class->name); + + // Force May + foreach ($object_class->getForceMayAttrs() as $attr_name) + if ($this->attributetypes->has(strtolower($attr_name->name))) + $this->attributetypes[strtolower($attr_name->name)]->setForceMay(); + } + + return $this->attributetypes; + + case 'ldapsyntaxes': + Log::debug('LDAP Syntaxes'); + + foreach ($schema->{$item} as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $o = new LDAPSyntax($line); + $this->ldapsyntaxes->put(strtolower($o->oid),$o); + } + + return $this->ldapsyntaxes; + + case 'matchingrules': + Log::debug('Matching Rules'); + $this->matchingruleuse = collect(); + + foreach ($schema->{$item} as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $o = new MatchingRule($line); + $this->matchingrules->put($o->name_lc,$o); + } + + /* + * For each MatchingRuleUse entry, add the attributes who use it to the + * MatchingRule in the $rules array. + */ + if ($schema->matchingruleuse) { + foreach ($schema->matchingruleuse as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $o = new MatchingRuleUse($line); + $this->matchingruleuse->put($o->name_lc,$o); + + if ($this->matchingrules->has($o->name_lc) !== FALSE) + $this->matchingrules[$o->name_lc]->setUsedByAttrs($o->getUsedByAttrs()); + } + + } else { + /* No MatchingRuleUse entry in the subschema, so brute-forcing + * the reverse-map for the "$rule->getUsedByAttrs()" data.*/ + foreach ($this->schema('attributetypes') as $attr) { + $rule_key = strtolower($attr->getEquality()); + + if ($this->matchingrules->has($rule_key) !== FALSE) + $this->matchingrules[$rule_key]->addUsedByAttr($attr->name); + } + } + + return $this->matchingrules; + + case 'objectclasses': + Log::debug('Object Classes'); + + foreach ($schema->{$item} as $line) { + if (is_null($line) || ! strlen($line)) + continue; + + $o = new ObjectClass($line,$this); + $this->objectclasses->put($o->name_lc,$o); + } + + // Now go through and reference the parent/child relationships + foreach ($this->objectclasses as $o) + foreach ($o->getSupClasses() as $parent) { + $parent = strtolower($parent); + if ($this->objectclasses->has($parent) !== FALSE) + $this->objectclasses[$parent]->addChildObjectClass($o->name); + } + + return $this->objectclasses; + } + }); + + return is_null($key) ? $result : $result->get($key); } public function schemaSyntaxName(string $oid): ?LDAPSyntax