From 8ec1d2b1fef3cdba05a3980157f052791634d718 Mon Sep 17 00:00:00 2001 From: Deon George Date: Tue, 14 Feb 2023 21:38:42 +1100 Subject: [PATCH] Ported the schema browser --- app/Classes/LDAP/Schema/AttributeType.php | 583 +++++++ app/Classes/LDAP/Schema/Base.php | 110 ++ app/Classes/LDAP/Schema/LDAPSyntax.php | 79 + app/Classes/LDAP/Schema/MatchingRule.php | 142 ++ app/Classes/LDAP/Schema/MatchingRuleUse.php | 99 ++ app/Classes/LDAP/Schema/ObjectClass.php | 529 ++++++ .../LDAP/Schema/ObjectClassAttribute.php | 41 + app/Classes/LDAP/Server.php | 290 +++- app/Exceptions/InvalidUsage.php | 10 + app/Http/Controllers/APIController.php | 27 + app/Http/Controllers/HomeController.php | 11 +- app/Ldap/Entry.php | 69 +- app/Providers/AppServiceProvider.php | 8 + config/config.php.example | 9 - config/pla.php | 16 + htdocs/schema.php | 640 ------- lib/ds_ldap.php | 852 --------- lib/schema_functions.php | 1539 ----------------- .../layouts/partials/contentheader.blade.php | 4 +- .../layouts/partials/htmlheader.blade.php | 3 + .../layouts/partials/sidebarmenu.blade.php | 2 - resources/views/debug.blade.php | 6 + resources/views/frames/schema.blade.php | 78 + .../frames/schema/attributetypes.blade.php | 153 ++ .../frames/schema/ldapsyntaxes.blade.php | 29 + .../frames/schema/matchingrules.blade.php | 101 ++ .../frames/schema/objectclasses.blade.php | 147 ++ resources/views/home.blade.php | 4 +- resources/views/layouts/dn.blade.php | 4 +- routes/api.php | 5 +- routes/web.php | 1 + 31 files changed, 2498 insertions(+), 3093 deletions(-) create mode 100644 app/Classes/LDAP/Schema/AttributeType.php create mode 100644 app/Classes/LDAP/Schema/Base.php create mode 100644 app/Classes/LDAP/Schema/LDAPSyntax.php create mode 100644 app/Classes/LDAP/Schema/MatchingRule.php create mode 100644 app/Classes/LDAP/Schema/MatchingRuleUse.php create mode 100644 app/Classes/LDAP/Schema/ObjectClass.php create mode 100644 app/Classes/LDAP/Schema/ObjectClassAttribute.php create mode 100644 app/Exceptions/InvalidUsage.php create mode 100644 config/pla.php delete mode 100644 htdocs/schema.php delete mode 100644 lib/schema_functions.php create mode 100644 resources/views/frames/schema.blade.php create mode 100644 resources/views/frames/schema/attributetypes.blade.php create mode 100644 resources/views/frames/schema/ldapsyntaxes.blade.php create mode 100644 resources/views/frames/schema/matchingrules.blade.php create mode 100644 resources/views/frames/schema/objectclasses.blade.php diff --git a/app/Classes/LDAP/Schema/AttributeType.php b/app/Classes/LDAP/Schema/AttributeType.php new file mode 100644 index 0000000..cec4b08 --- /dev/null +++ b/app/Classes/LDAP/Schema/AttributeType.php @@ -0,0 +1,583 @@ +children = collect(); + $this->aliases = collect(); + $this->used_in_object_classes = collect(); + $this->required_by_object_classes = collect(); + + for ($i=0; $i < count($strings); $i++) { + switch ($strings[$i]) { + case '(': + case ')': + break; + + case 'NAME': + // @note Some schema's return a (' instead of a ( ' + if ($strings[$i+1] != '(' && ! preg_match('/^\(/',$strings[$i+1])) { + do { + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + // This attribute has no aliases + //$this->aliases = collect(); + + } else { + $i++; + + do { + // In case we came here becaues of a (' + if (preg_match('/^\(/',$strings[$i])) + $strings[$i] = preg_replace('/^\(/','',$strings[$i]); + else + $i++; + + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + // Add alias names for this attribute + while ($strings[++$i] != ')') { + $alias = $strings[$i]; + $alias = preg_replace("/^\'(.*)\'$/",'$1',$alias); + $this->addAlias($alias); + } + } + + $this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name); + + Log::debug(sprintf('- Case NAME returned (%s)',$this->name),['aliases'=>$this->aliases]); + break; + + case 'DESC': + do { + $this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + $this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description); + + Log::debug(sprintf('- Case DESC returned (%s)',$this->description)); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + + Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete)); + break; + + case 'SUP': + $i++; + $this->sup_attribute = preg_replace("/^\'(.*)\'$/",'$1',$strings[$i]); + + Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_attribute)); + break; + + case 'EQUALITY': + $this->equality = $strings[++$i]; + + Log::debug(sprintf('- Case EQUALITY returned (%s)',$this->equality)); + break; + + case 'ORDERING': + $this->ordering = $strings[++$i]; + + Log::debug(sprintf('- Case ORDERING returned (%s)',$this->ordering)); + break; + + case 'SUBSTR': + $this->sub_str_rule = $strings[++$i]; + + Log::debug(sprintf('- Case SUBSTR returned (%s)',$this->sub_str_rule)); + break; + + case 'SYNTAX': + $this->syntax = $strings[++$i]; + $this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax); + Log::debug(sprintf('/ Evaluating SYNTAX returned (%s) [%s]',$this->syntax,$this->syntax_oid)); + + // Does this SYNTAX string specify a max length (ie, 1.2.3.4{16}) + $m = []; + if (preg_match('/{(\d+)}$/',$this->syntax,$m)) + $this->max_length = $m[1]; + else + $this->max_length = NULL; + + if ($i < count($strings) - 1 && $strings[$i+1] == '{') + do { + $this->name .= ' '.$strings[++$i]; + } while ($strings[$i] != '}'); + + $this->syntax = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax); + $this->syntax_oid = preg_replace("/^\'(.*)\'$/",'$1',$this->syntax_oid); + + Log::debug(sprintf('- Case SYNTAX returned (%s) [%s] {%d}',$this->syntax,$this->syntax_oid,$this->max_length)); + break; + + case 'SINGLE-VALUE': + $this->is_single_value = TRUE; + + Log::debug(sprintf('- Case SINGLE-VALUE returned (%s)',$this->is_single_value)); + break; + + case 'COLLECTIVE': + $this->is_collective = TRUE; + + Log::debug(sprintf('- Case COLLECTIVE returned (%s)',$this->is_collective)); + break; + + case 'NO-USER-MODIFICATION': + $this->is_no_user_modification = TRUE; + + Log::debug(sprintf('- Case NO-USER-MODIFICATION returned (%s)',$this->is_no_user_modification)); + break; + + case 'USAGE': + $this->usage = $strings[++$i]; + + Log::debug(sprintf('- Case USAGE returned (%s)',$this->usage)); + break; + + // @note currently not captured + case 'X-ORDERED': + Log::error(sprintf('- Case X-ORDERED returned (%s)',$strings[++$i])); + break; + + // @note currently not captured + case 'X-ORIGIN': + $value = ''; + + do { + $value .= (strlen($value) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + Log::error(sprintf('- Case X-ORIGIN returned (%s)',$value)); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) { + $this->oid = $strings[$i]; + Log::debug(sprintf('- Case default returned (%s)',$this->oid)); + + } elseif ($strings[$i]) + Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]); + } + } + } + + public function __clone() + { + // When we clone, we need to break the reference to + $this->aliases = clone $this->aliases; + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'aliases': return $this->aliases; + case 'children': return $this->children; + case 'forced_as_may': return $this->forced_as_may; + case 'is_collective': return $this->is_collective; + case 'is_no_user_modification': return $this->is_no_user_modification; + case 'is_single_value': return $this->is_single_value; + case 'equality': return $this->equality; + case 'max_length': return $this->max_length; + case 'ordering': return $this->ordering; + case 'sub_str_rule': return $this->sub_str_rule; + case 'sup_attribute': return $this->sup_attribute; + case 'syntax': return $this->syntax; + case 'syntax_oid': return $this->syntax_oid; + case 'type': return $this->type; + case 'usage': return $this->usage; + case 'used_in_object_classes': return $this->used_in_object_classes; + + default: return parent::__get($key); + } + } + + /** + * Adds an attribute name to the alias array. + * + * @param string $alias The name of a new attribute to add to this attribute's list of aliases. + */ + public function addAlias(string $alias): void + { + $this->aliases->push($alias); + } + + /** + * Children of this attribute type that inherit from this one + * + * @param string $child + * @return void + */ + public function addChild(string $child): void + { + $this->children->push($child); + } + + /** + * Adds an objectClass name to this attribute's list of "required by" objectClasses, + * that is the list of objectClasses which must have this attribute. + * + * @param string $name The name of the objectClass to add. + */ + public function addRequiredByObjectClass(string $name): void + { + if ($this->required_by_object_classes->search($name) === FALSE) + $this->required_by_object_classes->push($name); + } + + /** + * Adds an objectClass name to this attribute's list of "used in" objectClasses, + * that is the list of objectClasses which provide this attribute. + * + * @param string $name The name of the objectClass to add. + */ + public function addUsedInObjectClass(string $name): void + { + if ($this->used_in_object_classes->search($name) === FALSE) + $this->used_in_object_classes->push($name); + } + + /** + * Gets the names of attributes that are an alias for this attribute (if any). + * + * @return Collection An array of names of attributes which alias this attribute or + * an empty array if no attribute aliases this object. + * @deprecated use class->aliases + */ + public function getAliases(): Collection + { + return $this->aliases; + } + + /** + * Gets this attribute's equality string + * + * @return string + * @deprecated use $this->equality + */ + public function getEquality() + { + return $this->equality; + } + + /** + * Gets whether this attribute is collective. + * + * @return boolean Returns TRUE if this attribute is collective and FALSE otherwise. + * @deprecated use $this->is_collective + */ + public function getIsCollective(): bool + { + return $this->is_collective; + } + + /** + * Gets whether this attribute is not modifiable by users. + * + * @return boolean Returns TRUE if this attribute is not modifiable by users. + * @deprecated use $this->is_no_user_modification + */ + public function getIsNoUserModification(): bool + { + return $this->is_no_user_modification; + } + + /** + * Gets whether this attribute is single-valued. If this attribute only supports single values, TRUE + * is returned. If this attribute supports multiple values, FALSE is returned. + * + * @return boolean Returns TRUE if this attribute is single-valued or FALSE otherwise. + * @deprecated use class->is_single_value + */ + public function getIsSingleValue(): bool + { + return $this->is_single_value; + } + + /** + * Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, NULL is returned. + * + * @return int The maximum length (in characters) of this attribute or NULL if no maximum is specified. + * @deprecated use $this->max_length; + */ + public function getMaxLength() + { + return $this->max_length; + } + + /** + * Gets this attribute's ordering specification. + * + * @return string + * @deprecated use $this->ordering + */ + public function getOrdering(): string + { + return $this->ordering; + } + + /** + * Gets the list of "required by" objectClasses, that is the list of objectClasses + * which provide must have attribute. + * + * @return array An array of names of objectclasses (strings) which provide this attribute + */ + public function getRequiredByObjectClasses() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->required_by_object_classes); + + return $this->required_by_object_classes; + } + /** + * Gets this attribute's substring matching specification + * + * @return string + * @deprecated use $this->sub_str_rule; + */ + public function getSubstr() { + return $this->sub_str_rule; + } + + /** + * Gets this attribute's parent attribute (if any). If this attribute does not + * inherit from another attribute, NULL is returned. + * + * @return string + * @deprecated use $class->sup_attribute directly + */ + public function getSupAttribute() { + return $this->sup_attribute; + } + + /** + * Gets this attribute's syntax OID. Differs from getSyntaxString() in that this + * function only returns the actual OID with any length specification removed. + * Ie, if the syntax string is "1.2.3.4{16}", this function only retruns + * "1.2.3.4". + * + * @return string The syntax OID string. + * @deprecated use $this->syntax_oid; + */ + public function getSyntaxOID() + { + return $this->syntax_oid; + } + + /** + * Gets this attribute's raw syntax string (ie: "1.2.3.4{16}"). + * + * @return string The raw syntax string + */ + public function getSyntaxString() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax); + + return $this->syntax; + } + + /** + * Gets this attribute's type + * + * @return string The attribute's type. + * @deprecated use $this->type; + */ + public function getType() + { + return $this->type; + } + + /** + * Gets this attribute's usage string as defined by the LDAP server + * + * @return string + * @deprecated use $this->usage + */ + public function getUsage() + { + return $this->usage; + } + + /** + * Gets the list of "used in" objectClasses, that is the list of objectClasses + * which provide this attribute. + * + * @return Collection An array of names of objectclasses (strings) which provide this attribute + * @deprecated use $this->used_in_object_classes + */ + public function getUsedInObjectClasses(): Collection + { + return $this->used_in_object_classes; + } + + /** + * Returns whether the specified attribute is an alias for this one (based on this attribute's alias list). + * + * @param string $attr_name The name of the attribute to check. + * @return boolean TRUE if the specified attribute is an alias for this one, or FALSE otherwise. + */ + public function isAliasFor($attr_name) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); + + foreach ($this->aliases as $alias_attr_name) + if (strcasecmp($alias_attr_name,$attr_name) == 0) + return TRUE; + + return FALSE; + } + + /** + * @return bool + * @deprecated use $this->forced_as_may + */ + public function isForceMay(): bool + { + return $this->forced_as_may; + } + + /** + * Removes an attribute name from this attribute's alias array. + * + * @param string $alias The name of the attribute to remove. + */ + public function removeAlias(string $alias): void + { + if (($x=$this->aliases->search($alias)) !== FALSE) + $this->aliases->forget($x); + } + + /** + * Sets this attribute's list of aliases. + * + * @param Collection $aliases The array of alias names (strings) + * @deprecated use $this->aliases = + */ + public function setAliases(Collection $aliases): void + { + $this->aliases = $aliases; + } + + /** + * This function will mark this attribute as a forced MAY attribute + */ + public function setForceMay() { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->forced_as_may = TRUE; + } + + /** + * Sets whether this attribute is single-valued. + * + * @param boolean $is + */ + public function setIsSingleValue(bool $is): void + { + $this->is_single_value = $is; + } + + /** + * Sets this attribute's SUP attribute (ie, the attribute from which this attribute inherits). + * + * @param string $attr The name of the new parent (SUP) attribute + */ + public function setSupAttribute(string $attr): void + { + $this->sup_attribute = trim($attr); + } + + /** + * Sets this attribute's type. + * + * @param string $type The new type. + */ + public function setType($type) { + if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) + debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); + + $this->type = $type; + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Schema/Base.php b/app/Classes/LDAP/Schema/Base.php new file mode 100644 index 0000000..a835e2f --- /dev/null +++ b/app/Classes/LDAP/Schema/Base.php @@ -0,0 +1,110 @@ +line = $line; + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'description': return $this->description; + case 'is_obsolete': return $this->is_obsolete; + case 'line': return $this->line; + case 'name': return $this->name; + case 'name_lc': return strtolower($this->name); + case 'oid': return $this->oid; + + default: + throw new InvalidUsage('Unknown key: '.$key); + } + } + + /** + * @return string + * @deprecated replace with $class->description + */ + public function getDescription(): string + { + return $this->description; + } + + /** + * Gets whether this item is flagged as obsolete by the LDAP server. + * + * @deprecated replace with $this->is_obsolete + */ + public function getIsObsolete(): bool + { + return $this->is_obsolete; + } + + /** + * Return the objects name. + * + * @param boolean $lower Return the name in lower case (default) + * @return string The name + * @deprecated use object->name + */ + public function getName(bool $lower=TRUE): string + { + return $lower ? strtolower($this->name) : $this->name; + } + + /** + * Return the objects name. + * + * @return string The name + * @deprecated use object->oid + */ + public function getOID(): string + { + return $this->oid; + } + + public function setDescription(string $desc): void + { + $this->description = $desc; + } + + /** + * Sets this attribute's name. + * + * @param string $name The new name to give this attribute. + */ + public function setName($name): void + { + $this->name = $name; + } + + public function setOID(string $oid): void + { + $this->oid = $oid; + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Schema/LDAPSyntax.php b/app/Classes/LDAP/Schema/LDAPSyntax.php new file mode 100644 index 0000000..046abe7 --- /dev/null +++ b/app/Classes/LDAP/Schema/LDAPSyntax.php @@ -0,0 +1,79 @@ +description .= (strlen($this->description) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + $this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description); + + Log::debug(sprintf('- Case DESC returned (%s)',$this->description)); + break; + + case 'X-BINARY-TRANSFER-REQUIRED': + $this->binary_transfer_required = (str_replace("'",'',$strings[++$i]) === 'TRUE'); + + Log::debug(sprintf('- Case X-BINARY-TRANSFER-REQUIRED returned (%s)',$this->binary_transfer_required)); + break; + + case 'X-NOT-HUMAN-READABLE': + $this->is_not_human_readable = (str_replace("'",'',$strings[++$i]) === 'TRUE'); + + Log::debug(sprintf('- Case X-NOT-HUMAN-READABLE returned (%s)',$this->is_not_human_readable)); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) { + $this->oid = $strings[$i]; + Log::debug(sprintf('- Case default returned (%s)',$this->oid)); + + } elseif ($strings[$i]) + Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]); + } + } + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'binary_transfer_required': return $this->binary_transfer_required; + case 'is_not_human_readable': return $this->is_not_human_readable; + + default: return parent::__get($key); + } + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Schema/MatchingRule.php b/app/Classes/LDAP/Schema/MatchingRule.php new file mode 100644 index 0000000..5492421 --- /dev/null +++ b/app/Classes/LDAP/Schema/MatchingRule.php @@ -0,0 +1,142 @@ +used_by_attrs = collect(); + + for ($i=0; $iname .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + } else { + $i++; + + do { + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + do { + $i++; + + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace("/^\'/",'',$this->name); + $this->name = preg_replace("/\'$/",'',$this->name); + + Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name))); + break; + + case 'DESC': + do { + $this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + $this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description); + + Log::debug(sprintf('- Case DESC returned (%s)',$this->description)); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + + Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete)); + break; + + case 'SYNTAX': + $this->syntax = $strings[++$i]; + + Log::debug(sprintf('- Case SYNTAX returned (%s)',$this->syntax)); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) { + $this->oid = $strings[$i]; + Log::debug(sprintf('- Case default returned (%s)',$this->oid)); + + } elseif ($strings[$i]) + Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]); + } + } + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'syntax': return $this->syntax; + case 'used_by_attrs': return $this->used_by_attrs; + + default: return parent::__get($key); + } + } + + /** + * Adds an attribute name to the list of attributes who use this MatchingRule + */ + public function addUsedByAttr(string $name): void + { + $name = trim($name); + + if ($this->used_by_attrs->search($name) === FALSE) + $this->used_by_attrs->push($name); + } + + /** + * Gets an array of attribute names (strings) which use this MatchingRule + * + * @return array The array of attribute names (strings). + * @deprecated use $this->used_by_attrs + */ + public function getUsedByAttrs() + { + return $this->used_by_attrs; + } + + /** + * Sets the list of used_by_attrs to the array specified by $attrs; + * + * @param Collection $attrs The array of attribute names (strings) which use this MatchingRule + */ + public function setUsedByAttrs(Collection $attrs): void + { + $this->used_by_attrs = $attrs; + } +} diff --git a/app/Classes/LDAP/Schema/MatchingRuleUse.php b/app/Classes/LDAP/Schema/MatchingRuleUse.php new file mode 100644 index 0000000..cdbd2c8 --- /dev/null +++ b/app/Classes/LDAP/Schema/MatchingRuleUse.php @@ -0,0 +1,99 @@ +used_by_attrs = collect(); + + for ($i=0; $iname .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + } else { + $i++; + + do { + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match("/\'$/s",$strings[$i])); + + do { + $i++; + + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name); + + Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name))); + break; + + case 'APPLIES': + if ($strings[$i+1] != '(') { + // Has a single attribute name + $this->used_by_attrs = collect($strings[++$i]); + + } else { + // Has multiple attribute names + while ($strings[++$i] != ')') { + $new_attr = $strings[++$i]; + $new_attr = preg_replace("/^\'(.*)\'$/",'$1',$new_attr); + + $this->used_by_attrs->push($new_attr); + } + } + + Log::debug(sprintf('- Case APPLIES returned (%s)',$this->used_by_attrs->join(','))); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) { + $this->oid = $strings[$i]; + Log::debug(sprintf('- Case default returned (%s)',$this->oid)); + + } elseif ($strings[$i]) + Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]); + } + } + } + + /** + * Gets an array of attribute names (strings) which use this MatchingRuleUse object. + * + * @return array The array of attribute names (strings). + * @deprecated use $this->used_by_attrs + */ + public function getUsedByAttrs() + { + return $this->used_by_attrs; + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Schema/ObjectClass.php b/app/Classes/LDAP/Schema/ObjectClass.php new file mode 100644 index 0000000..68a7cda --- /dev/null +++ b/app/Classes/LDAP/Schema/ObjectClass.php @@ -0,0 +1,529 @@ +connection = $entry->getConnection(); + $this->server = $server; + $this->may_attrs = collect(); + $this->may_force = collect(); + $this->must_attrs = collect(); + $this->sup_classes = collect(); + $this->child_objectclasses = collect(); + + for ($i=0; $i < count($strings); $i++) { + switch ($strings[$i]) { + case '(': + case ')': + break; + + case 'NAME': + if ($strings[$i+1] != '(') { + do { + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + } else { + $i++; + + do { + $this->name .= (strlen($this->name) ? ' ' : '').$strings[++$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + do { + $i++; + } while (! preg_match('/\)+\)?/',$strings[$i])); + } + + $this->name = preg_replace("/^\'(.*)\'$/",'$1',$this->name); + + Log::debug(sprintf(sprintf('- Case NAME returned (%s)',$this->name))); + break; + + case 'DESC': + do { + $this->description .= (strlen($this->description) ? ' ' : '').$strings[++$i]; + + } while (! preg_match('/\'$/s',$strings[$i])); + + $this->description = preg_replace("/^\'(.*)\'$/",'$1',$this->description); + + Log::debug(sprintf('- Case DESC returned (%s)',$this->description)); + break; + + case 'OBSOLETE': + $this->is_obsolete = TRUE; + + Log::debug(sprintf('- Case OBSOLETE returned (%s)',$this->is_obsolete)); + break; + + case 'SUP': + if ($strings[$i+1] != '(') { + $this->sup_classes->push(preg_replace("/'/",'',$strings[++$i])); + + } else { + $i++; + + do { + $i++; + + if ($strings[$i] != '$') + $this->sup_classes->push(preg_replace("/'/",'',$strings[$i])); + + } while (! preg_match('/\)+\)?/',$strings[$i+1])); + } + + Log::debug(sprintf('- Case SUP returned (%s)',$this->sup_classes->join(','))); + break; + + case 'ABSTRACT': + $this->type = self::OC_ABSTRACT; + + Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type)); + break; + + case 'STRUCTURAL': + $this->type = self::OC_STRUCTURAL; + + Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type)); + break; + + case 'AUXILIARY': + $this->type = self::OC_AUXILIARY; + + Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type)); + break; + + case 'MUST': + $attrs = collect(); + + $i = $this->parseList(++$i,$strings,$attrs); + + Log::debug(sprintf('= parseList returned %d (%s)',$i,$attrs->join(','))); + + foreach ($attrs as $string) { + $attr = new ObjectClassAttribute($string,$this->name); + + if ($server->isForceMay($attr->getName())) { + $this->may_force->push($attr); + $this->may_attrs->push($attr); + + } else + $this->must_attrs->push($attr); + } + + Log::debug(sprintf('- Case MUST returned (%s) (%s)',$this->must_attrs->join(','),$this->may_force->join(','))); + break; + + case 'MAY': + $attrs = collect(); + + $i = $this->parseList(++$i,$strings,$attrs); + + Log::debug(sprintf('parseList returned %d (%s)',$i,$attrs->join(','))); + + foreach ($attrs as $string) { + $attr = new ObjectClassAttribute($string,$this->name); + $this->may_attrs->push($attr); + } + + Log::debug(sprintf('- Case MAY returned (%s)',$this->may_attrs->join(','))); + break; + + default: + if (preg_match('/[\d\.]+/i',$strings[$i]) && ($i === 1)) { + $this->oid = $strings[$i]; + Log::debug(sprintf('- Case default returned (%s)',$this->oid)); + + } elseif ($strings[$i]) + Log::alert(sprintf('! Case default discovered a value NOT parsed (%s)',$strings[$i]),['line'=>$line]); + } + } + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'sup': + return $this->sup_classes; + + case 'type_name': + switch ($this->type) { + case self::OC_STRUCTURAL: return 'Structural'; + case self::OC_ABSTRACT: return 'Abstract'; + case self::OC_AUXILIARY: return 'Auxiliary'; + default: + throw new InvalidUsage('Unknown ObjectClass Type: '.$this->type); + } + + default: return parent::__get($key); + } + } + + /** + * Adds an objectClass to the list of objectClasses that inherit + * from this objectClass. + * + * @param String $name The name of the objectClass to add + */ + public function addChildObjectClass(string $name): void + { + if ($this->child_objectclasses->search($name) === FALSE) { + $this->child_objectclasses->push($name); + } + } + + /** + * Behaves identically to addMustAttrs, but it operates on the MAY + * attributes of this objectClass. + * + * @param array $attr An array of attribute names (strings) to add. + */ + private function addMayAttrs(array $attr): void + { + if (! is_array($attr) || ! count($attr)) + return; + + $this->may_attrs = $this->may_attrs->merge($attr)->unique(); + } + + /** + * Adds the specified array of attributes to this objectClass' list of + * MUST attributes. The resulting array of must attributes will contain + * unique members. + * + * @param array $attr An array of attribute names (strings) to add. + */ + private function addMustAttrs(array $attr): void + { + if (! is_array($attr) || ! count($attr)) + return; + + $this->must_attrs = $this->must_attrs->merge($attr)->unique(); + } + + /** + * @return Collection + * @deprecated use $this->may_force + */ + public function getForceMayAttrs(): Collection + { + return $this->may_force; + } + + /** + * Gets an array of AttributeType objects that entries of this ObjectClass may define. + * This differs from getMayAttrNames in that it returns an array of AttributeType objects + * + * @param bool $parents Also get the may attrs of our parents. + * @return Collection The array of allowed AttributeType objects. + * + * @throws InvalidUsage + * @see getMustAttrNames + * @see getMustAttrs + * @see getMayAttrNames + * @see AttributeType + */ + public function getMayAttrs(bool $parents=FALSE): Collection + { + // If we dont need our parents, then we'll just return ours. + if (! $parents) + return $this->may_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); }); + + $attrs = $this->may_attrs; + + foreach ($this->getParents() as $object_class) { + $sc = $this->server->schema('objectclasses',$object_class); + $attrs = $attrs->merge($sc->getMayAttrs($parents)); + } + + // Remove any duplicates + $attrs = $attrs->unique(function($item) { return $item->name; }); + + // Return a sorted list + return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); }); + } + + /** + * Gets an array of attribute names (strings) that entries of this ObjectClass must define. + * This differs from getMayAttrs in that it returns an array of strings rather than + * array of AttributeType objects + * + * @param bool $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass provides. + * @return Collection The array of allowed attribute names (strings). + * + * @throws InvalidUsage + * @see getMustAttrs + * @see getMayAttrs + * @see getMustAttrNames + */ + public function getMayAttrNames(bool $parents=FALSE): Collection + { + return $this->getMayAttrs($parents)->ppluck('name'); + } + + /** + * Gets an array of AttributeType objects that entries of this ObjectClass must define. + * This differs from getMustAttrNames in that it returns an array of AttributeType objects + * + * @param bool $parents Also get the must attrs of our parents. + * @return Collection The array of required AttributeType objects. + * + * @throws InvalidUsage + * @see getMustAttrNames + * @see getMayAttrs + * @see getMayAttrNames + */ + public function getMustAttrs(bool $parents=FALSE): Collection + { + // If we dont need our parents, then we'll just return ours. + if (! $parents) + return $this->must_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); }); + + $attrs = $this->must_attrs; + + foreach ($this->getParents() as $object_class) { + $sc = $this->server->schema('objectclasses',$object_class); + $attrs = $attrs->merge($sc->getMustAttrs($parents)); + } + + // Remove any duplicates + $attrs = $attrs->unique(function($item) { return $item->name; }); + + // Return a sorted list + return $attrs->sortBy(function($item) { return strtolower($item->name.$item->source); }); + } + + /** + * Gets an array of attribute names (strings) that entries of this ObjectClass must define. + * This differs from getMustAttrs in that it returns an array of strings rather than + * array of AttributeType objects + * + * @param bool $parents An array of ObjectClass objects to use when traversing + * the inheritance tree. This presents some what of a bootstrapping problem + * as we must fetch all objectClasses to determine through inheritance which + * attributes this objectClass provides. + * @return Collection The array of allowed attribute names (strings). + * + * @throws InvalidUsage + * @see getMustAttrs + * @see getMayAttrs + * @see getMayAttrNames + */ + public function getMustAttrNames(bool $parents=FALSE): Collection + { + return $this->getMustAttrs($parents)->ppluck('name'); + } + + /** + * This will return all our parent ObjectClass Objects + */ + public function getParents(): Collection + { + // If the only class is 'top', then we have no more parents + if (($this->sup_classes->count() === 1) && (strtolower($this->sup_classes->first()) === 'top')) + return collect(); + + $result = collect(); + + foreach ($this->sup_classes as $object_class) { + $result->push($object_class); + + $oc = $this->server->schema('objectclasses',$object_class); + + if ($oc) + $result = $result->merge($oc->getParents()); + } + + return $result; + } + + /** + * Determine if an array is listed in the may_force attrs + */ + public function isForceMay(string $attr): bool + { + return $this->may_force->ppluck('name')->contains($attr); + } + + /** + * Return if this objectClass is related to $oclass + * + * @param array $oclass ObjectClasses that this attribute may be related to + * @return bool + * @throws InvalidUsage + */ + public function isRelated(array $oclass): bool + { + // If I am in the array, we'll just return false + if (in_array_ignore_case($this->name,$oclass)) + return FALSE; + + foreach ($oclass as $object_class) { + $oc = $this->server->schema('objectclasses',$object_class); + + if ($oc->isStructural() && in_array_ignore_case($this->name,$oc->getParents())) + return TRUE; + } + + return FALSE; + } + + public function isStructural(): bool + { + return $this->type === self::OC_STRUCTURAL; + } + + /** + * Parse an LDAP schema list + * + * A list starts with a ( followed by a list of attributes separated by $ terminated by ) + * The first token can therefore be a ( or a (NAME or a (NAME) + * The last token can therefore be a ) or NAME) + * The last token may be terminated by more than one bracket + */ + private function parseList(int $i,array $strings,Collection &$attrs): int + { + $string = $strings[$i]; + + if (! preg_match('/^\(/',$string)) { + // A bareword only - can be terminated by a ) if the last item + if (preg_match('/\)+$/',$string)) + $string = preg_replace('/\)+$/','',$string); + + $attrs->push($string); + + } elseif (preg_match('/^\(.*\)$/',$string)) { + $string = preg_replace('/^\(/','',$string); + $string = preg_replace('/\)+$/','',$string); + + $attrs->push($string); + + } else { + // Handle the opening cases first + if ($string === '(') { + $i++; + + } elseif (preg_match('/^\(./',$string)) { + $string = preg_replace('/^\(/','',$string); + $attrs->push($string); + $i++; + } + + // Token is either a name, a $ or a ')' + // NAME can be terminated by one or more ')' + while (! preg_match('/\)+$/',$strings[$i])) { + $string = $strings[$i]; + + if ($string === '$') { + $i++; + continue; + } + + if (preg_match('/\)$/',$string)) + $string = preg_replace('/\)+$/','',$string); + else + $i++; + + $attrs->push($string); + } + } + + $attrs = $attrs->sort(); + + return $i; + } + + /** + * Returns the array of objectClass names which inherit from this objectClass. + * + * @return Collection Names of objectClasses which inherit from this objectClass. + * @deprecated use $this->child_objectclasses + */ + public function getChildObjectClasses(): Collection + { + return $this->child_objectclasses; + } + + /** + * Gets the objectClass names from which this objectClass inherits. + * + * @return array An array of objectClass names (strings) + * @deprecated use $this->sup_classes; + */ + public function getSupClasses() { + return $this->sup_classes; + } + + /** + * Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY. + * + * @deprecated use $this->type_name + */ + public function getType() + { + return $this->type; + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Schema/ObjectClassAttribute.php b/app/Classes/LDAP/Schema/ObjectClassAttribute.php new file mode 100644 index 0000000..62e92ff --- /dev/null +++ b/app/Classes/LDAP/Schema/ObjectClassAttribute.php @@ -0,0 +1,41 @@ +name = $name; + $this->source = $source; + } + + public function __get(string $key): mixed + { + switch ($key) { + case 'source': + return $this->source; + + default: return parent::__get($key); + } + } +} \ No newline at end of file diff --git a/app/Classes/LDAP/Server.php b/app/Classes/LDAP/Server.php index 3b53039..6b64af4 100644 --- a/app/Classes/LDAP/Server.php +++ b/app/Classes/LDAP/Server.php @@ -5,21 +5,38 @@ namespace App\Classes\LDAP; use Carbon\Carbon; use Exception; use Illuminate\Support\Arr; +use Illuminate\Support\Collection as ArrayCollection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; -use LdapRecord\Models\Model; +use Illuminate\Support\Facades\Log; use LdapRecord\Query\Collection; -use LdapRecord\Query\Model\Builder; +use App\Classes\LDAP\Schema\{AttributeType,Base,LDAPSyntax,MatchingRule,MatchingRuleUse,ObjectClass}; +use App\Exceptions\InvalidUsage; use App\Ldap\Entry; class Server { + // This servers schema objectclasses + private ArrayCollection $attributetypes; + private ArrayCollection $ldapsyntaxes; + private ArrayCollection $matchingrules; + private ArrayCollection $matchingruleuse; + private ArrayCollection $objectclasses; + + // Valid items that can be fetched + public const schema_types = [ + 'objectclasses', + 'attributetypes', + 'ldapsyntaxes', + 'matchingrules', + ]; + /** - * Query the server for a DN and return it's children and if those children have children. + * Query the server for a DN and return its children and if those children have children. * * @param string $dn - * @return array|Collection|null + * @return Collection|null */ public function children(string $dn): ?Collection { @@ -37,7 +54,7 @@ class Server * * @param string $dn * @param array $attrs - * @return array|Model|Collection|Builder|null + * @return Entry|null */ public function fetch(string $dn,array $attrs=['*','+']): ?Entry { @@ -103,4 +120,265 @@ class Server ($key == 'desc' ? 'No description available, can you help with one?' : ($key == 'title' ? $oid : NULL)) ); } -} + + /** + * This function determines if the specified attribute is contained in the force_may list + * as configured in config.php. + * + * @return boolean True if the specified attribute is configured to be force as a may attribute + */ + public function isForceMay($attr_name): bool + { + return in_array($attr_name,config('pla.force_may',[])); + } + + /** + * Return the server's schema + * + * @param string $item Schema Item to Fetch + * @param string|null $key + * @return ArrayCollection|Base + * @throws InvalidUsage + */ + public function schema(string $item,string $key=NULL): ArrayCollection|Base|NULL + { + // Ensure our item to fetch is lower case + $item = strtolower($item); + if ($key) + $key = strtolower($key); + + // This error message is not localized as only developers should ever see it + 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(); + + break; + + case 'ldapsyntaxes': + if (isset($this->ldapsyntaxes)) + return is_null($key) ? $this->ldapsyntaxes : $this->ldapsyntaxes->get($key); + else + $this->ldapsyntaxes = collect(); + + break; + + case 'matchingrules': + if (isset($this->matchingrules)) + return is_null($key) ? $this->matchingrules : $this->matchingrules->get($key); + 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); + } + + /* + * 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 is_null($key) ? $this->matchingrules : $this->matchingrules->get($key); + } + + return NULL; + } + + public function schemaSyntaxName(string $oid): ?LDAPSyntax + { + return $this->schema('ldapsyntaxes',$oid); + } +} \ No newline at end of file diff --git a/app/Exceptions/InvalidUsage.php b/app/Exceptions/InvalidUsage.php new file mode 100644 index 0000000..dffcda5 --- /dev/null +++ b/app/Exceptions/InvalidUsage.php @@ -0,0 +1,10 @@ +type) { + case 'objectclasses': + return view('frames.schema.objectclasses') + ->with('objectclasses',$server->schema('objectclasses')->sortBy(function($item) { return strtolower($item->name); })); + + case 'attributetypes': + return view('frames.schema.attributetypes') + ->with('server',$server) + ->with('attributetypes',$server->schema('attributetypes')->sortBy(function($item) { return strtolower($item->name); })); + + case 'ldapsyntaxes': + return view('frames.schema.ldapsyntaxes') + ->with('ldapsyntaxes',$server->schema('ldapsyntaxes')->sortBy(function($item) { return strtolower($item->description); })); + + case 'matchingrules': + return view('frames.schema.matchingrules') + ->with('matchingrules',$server->schema('matchingrules')->sortBy(function($item) { return strtolower($item->name); })); + + default: + abort(404); + } + } } diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index a19ecf7..4217c92 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -8,7 +8,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\File; -use LdapRecord\Models\ModelNotFoundException; +use LdapRecord\Query\ObjectNotFoundException; use App\Ldap\Entry; use App\Classes\LDAP\Server; @@ -49,7 +49,7 @@ class HomeController extends Controller * LDAP Server INFO * * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View - * @throws ModelNotFoundException + * @throws ObjectNotFoundException */ public function info() { @@ -74,7 +74,7 @@ class HomeController extends Controller }); // @todo If we cant get server info, we should probably show a nice error dialog - } catch (ModelNotFoundException $e) { + } catch (ObjectNotFoundException $e) { $attrs = collect(); } @@ -98,6 +98,11 @@ class HomeController extends Controller ->with('dn',$dn); } + public function schema_frame() + { + return view('frames.schema'); + } + /** * Sort the attributes * diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index cb3c1fd..76ba501 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -14,13 +14,6 @@ use App\Classes\LDAP\Attribute\Factory; class Entry extends Model { - /** - * The object classes of the LDAP model. - * - * @var array - */ - public static $objectClasses = []; - /* OVERRIDES */ public function getAttributes(): array @@ -39,23 +32,18 @@ class Entry extends Model * Gets the root DN of the specified LDAPServer, or throws an exception if it * can't find it. * - * @param null $connection + * @param null $connection Return a collection of baseDNs + * @param bool $objects Return a collection of Entry Models * @return Collection * @throws ObjectNotFoundException * @testedin GetBaseDNTest::testBaseDNExists(); */ - public static function baseDNs($connection = NULL): Collection + public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection { $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); try { - $base = static::on($connection ?? (new static)->getConnectionName()) - ->cache($cachetime) - ->in(NULL) - ->read() - ->select(['namingcontexts']) - ->whereHas('objectclass') - ->firstOrFail(); + $base = self::rootDSE($connection,$cachetime); /** * LDAP Error Codes: @@ -160,6 +148,9 @@ class Entry extends Model } } + if (! $objects) + return collect($base->namingcontexts); + /** * @note While we are caching our baseDNs, it seems if we have more than 1, * our caching doesnt generate a hit on a subsequent call to this function (before the cache expires). @@ -174,6 +165,32 @@ class Entry extends Model return $result; } + /** + * Obtain the rootDSE for the server, that gives us server information + * + * @param null $connection + * @return Entry|null + * @throws ObjectNotFoundException + * @testedin TranslateOidTest::testRootDSE(); + */ + public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model + { + return static::on($connection ?? (new static)->getConnectionName()) + ->cache($cachetime) + ->in(NULL) + ->read() + ->select(['+']) + ->whereHas('objectclass') + ->firstOrFail(); + } + + public static function schemaDN($connection = NULL): string + { + $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); + + return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first(); + } + /* ATTRIBUTES */ /** @@ -254,22 +271,4 @@ class Entry extends Model // Default return 'fa-fw fas fa-cog'; } - - /** - * Obtain the rootDSE for the server, that gives us server information - * - * @param null $connection - * @return Entry|null - * @throws ObjectNotFoundException - * @testedin TranslateOidTest::testRootDSE(); - */ - public function rootDSE($connection = NULL): ?Entry - { - return static::on($connection ?? (new static)->getConnectionName()) - ->in(NULL) - ->read() - ->select(['+']) - ->whereHas('objectclass') - ->firstOrFail(); - } -} +} \ No newline at end of file diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index b5540ce..475ba19 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Illuminate\Support\Collection; use Illuminate\Support\ServiceProvider; use LdapRecord\Configuration\DomainConfiguration; use LdapRecord\Laravel\LdapRecord; @@ -32,5 +33,12 @@ class AppServiceProvider extends ServiceProvider public function boot() { $this->loadViewsFrom(__DIR__.'/../../resources/themes/architect/views/','architect'); + + // Enable pluck on collections to work on private values + Collection::macro('ppluck', function ($attr) { + return $this->map(function (object $item) use ($attr) { + return $item->{$attr}; + })->values(); + }); } } \ No newline at end of file diff --git a/config/config.php.example b/config/config.php.example index 973eef3..5c3b126 100644 --- a/config/config.php.example +++ b/config/config.php.example @@ -492,15 +492,6 @@ $servers->setValue('server','name','My LDAP Server'); // $servers->setValue('server','custom_attrs',array('')); # $servers->setValue('server','custom_attrs',array('nsRoleDN','nsRole','nsAccountLock')); -/* These attributes will be forced to MAY attributes and become option in the - templates. If they are not defined in the templates, then they wont appear - as per normal template processing. You may want to do this because your LDAP - server may automatically calculate a default value. - In Fedora Directory Server using the DNA Plugin one could ignore uidNumber, - gidNumber and sambaSID. */ -// $servers->setValue('server','force_may',array('')); -# $servers->setValue('server','force_may',array('uidNumber','gidNumber','sambaSID')); - /********************************************* * Unique attributes * *********************************************/ diff --git a/config/pla.php b/config/pla.php new file mode 100644 index 0000000..82b9d6e --- /dev/null +++ b/config/pla.php @@ -0,0 +1,16 @@ + ['uidNumber','gidNumber','sambaSID'], + */ + 'force_may' => [], +]; \ No newline at end of file diff --git a/htdocs/schema.php b/htdocs/schema.php deleted file mode 100644 index aa4a749..0000000 --- a/htdocs/schema.php +++ /dev/null @@ -1,640 +0,0 @@ -%s.

%s
  • %s
  • %s
  • %s
  • %s
', - _('Could not retrieve schema from'),$app['server']->getName(), - _('This could happen for several reasons, the most probable of which are:'),_('The server does not fully support the LDAP protocol.'), - _('Your version of PHP does not correctly perform the query.'),_('phpLDAPadmin doesn\'t know how to fetch the schema for your server.'), - _('Or lastly, your LDAP server doesnt provide this information.')); - -printf('

%s %s

',_('Schema for server'),$app['server']->getName()); - -$entry['schema_types'] = array( - 'objectclasses'=>_('ObjectClasses'), - 'attributes'=>_('Attribute Types'), - 'syntaxes'=>_('Syntaxes'), - 'matching_rules'=>_('Matching Rules')); - -echo '
'; -echo '
'; - -$counter = 0; -foreach ($entry['schema_types'] as $item => $value) { - if ($counter++) - echo ' | '; - - $entry['href'][$item] = sprintf('cmd=schema&server_id=%s&view=%s',$app['server']->getIndex(),$item); - - if ($entry['view'] == $item) { - echo _($value); - - } else { - if (isAjaxEnabled()) - printf('%s', - htmlspecialchars($entry['href'][$item]),htmlspecialchars($entry['href'][$item]),$value,$value,$value); - else - printf('%s',htmlspecialchars($entry['href'][$item]),_($value)); - } -} - -echo '
'; -echo '
'; - -switch($entry['view']) { - case 'syntaxes': - $highlight_oid = get_request('highlight_oid','GET',false,false); - - echo ''; - printf('',_('Syntax OID'),_('Description')); - - $counter = 1; - - $schema_syntaxes = $app['server']->SchemaSyntaxes(); - if (! $schema_syntaxes) - error($schema_error_str,'error','index.php'); - - foreach ($schema_syntaxes as $syntax) { - $counter++; - $oid = $syntax->getOID(); - $desc = $syntax->getDescription(); - - if ($highlight_oid && $highlight_oid == $oid) - echo ''; - - else - printf('',$counter%2==0?'even':'odd'); - - printf('',$oid,$desc); - } - - echo '
%s%s
%s%s
'; - break; - - case 'attributes': - $entry['attr_types'] = array( - 'desc' => _('Description'), - 'obsolete' => _('Obsolete'), - 'inherits' => _('Inherits from'), - 'equality' => _('Equality'), - 'ordering' => _('Ordering'), - 'substring_rule' => _('Substring Rule'), - 'syntax' => _('Syntax'), - 'single_valued' => _('Single Valued'), - 'collective' => _('Collective'), - 'user_modification' => _('User Modification'), - 'usage' => _('Usage'), - 'maximum_length' => _('Maximum Length'), - 'aliases' => _('Aliases'), - 'used_by_objectclasses' => _('Used by objectClasses'), - 'force_as_may' => _('Force as MAY by config') - ); - - $sattrs = $app['server']->SchemaAttributes(); - - if (! $sattrs || ! $app['server']->SchemaObjectClasses()) - error($schema_error_str,'error','index.php'); - - printf('%s:',_('Jump to an attribute type')); - echo '
'; - echo '
'; - echo ''; - printf('',$entry['view']); - printf('',$app['server']->getIndex()); - - if (isAjaxEnabled()) { - drawJSItems($sattrs); - echo ''; - - echo ''; - foreach ($sattrs as $name => $attr) - printf('', - $name,$name == $entry['value'] ? 'selected="selected" ': '',$attr->getName(false)); - echo ''; - - if (isAjaxEnabled()) - printf('',_('Go')); - else - printf('',_('Go')); - echo '
'; - echo '
'; - echo '
'; - - foreach ($sattrs as $attr) { - if (isAjaxEnabled() || (is_null($entry['value']) || ! trim($entry['value']) || $entry['value']==$attr->getName())) { - if ((! is_null($entry['value']) && $entry['value']==$attr->getName()) || ! trim($entry['value'])) - $entry['viewed'] = true; - - if (isAjaxEnabled() && $entry['value']) - printf('
',$attr->getName(),strcasecmp($entry['value'],$attr->getName()) ? 'none' : 'block'); - else - printf('
',$attr->getName()); - - echo ''; - printf('', - $attr->getName(),$attr->getName(false)); - - $counter = 0; - - foreach ($entry['attr_types'] as $item => $value) { - - printf('',++$counter%2 ? 'odd' : 'even'); - printf('',$value); - - switch ($item) { - case 'desc': - printf('', - is_null($attr->getDescription()) ? - '('._('no description').')' : $attr->getDescription()); - - echo ''; - printf('',++$counter%2 ? 'odd' : 'even'); - echo ''; - printf('',$attr->getOID()); - - break; - - case 'obsolete': - printf('',$attr->getIsObsolete() ? ''._('Yes').'' : _('No')); - break; - - case 'inherits': - echo ''; - break; - - case 'equality': - echo ''; - break; - - case 'ordering': - printf('', - is_null($attr->getOrdering()) ? '('._('not specified').')' : $attr->getOrdering()); - break; - - case 'substring_rule': - printf('', - is_null($attr->getSubstr()) ? '('._('not specified').')' : $attr->getSubstr()); - break; - - case 'syntax': - echo ''; - break; - - case 'single_valued': - printf('',$attr->getIsSingleValue() ? _('Yes') : _('No')); - break; - - case 'collective': - printf('',$attr->getIsCollective() ? _('Yes') : _('No')); - break; - - case 'user_modification': - printf('',$attr->getIsNoUserModification() ? _('No') : _('Yes')); - break; - - case 'usage': - printf('',$attr->getUsage() ? $attr->getUsage() : '('._('not specified').')'); - break; - - case 'maximum_length': - echo ''; - break; - - case 'aliases': - echo ''; - break; - - case 'used_by_objectclasses': - echo ''; - break; - - case 'force_as_may': - printf('',$attr->isForceMay() ? _('Yes') : _('No')); - break; - - } - echo ''; - } - echo '
%s
%s%s
OID%s%s'; - - if (is_null($attr->getSupAttribute())) - printf('(%s)',_('none')); - - else { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['attributes'],strtolower($attr->getSupAttribute()))); - if (isAjaxEnabled()) - printf('%s', - $href,strtolower($attr->getSupAttribute()),$attr->getSupAttribute()); - else - printf('%s',$href,$attr->getSupAttribute()); - } - - echo ''; - - if (is_null($attr->getEquality())) - printf('(%s)',_('not specified')); - - else { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['matching_rules'],$attr->getEquality())); - if (isAjaxEnabled()) - printf('%s', - $href,$href,_('Matching Rules'),$attr->getEquality(),$attr->getEquality()); - else - printf('%s',$href,$attr->getEquality()); - } - - echo '%s%s'; - - if (is_null($attr->getType())) { - echo $attr->getSyntaxOID(); - - } else { - $href = htmlspecialchars(sprintf('%s&highlight_oid=%s',$entry['href']['syntaxes'],$attr->getSyntaxOID())); - if (isAjaxEnabled()) - printf('%s (%s)', - $href,$href,_('Syntaxes'),'',$attr->getType(),$attr->getSyntaxOID()); - else - printf('%s (%s)',$href,$attr->getType(),$attr->getSyntaxOID()); - } - - echo '%s%s%s%s'; - - if ( is_null($attr->getMaxLength())) - echo '('._('not applicable').')'; - - else - printf('%s %s',number_format($attr->getMaxLength()), - $attr->getMaxLength()>1 ? _('characters') : _('character')); - - echo ''; - - if (count($attr->getAliases()) == 0) - printf('(%s)',_('none')); - - else - foreach ($attr->getAliases() as $alias) { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['attributes'],strtolower($alias))); - if (isAjaxEnabled()) - printf('%s', - $href,strtolower($alias),$alias); - else - printf('%s',$href,$alias); - } - - echo ''; - - if (count($attr->getUsedInObjectClasses()) == 0) - printf('(%s)',_('none')); - - else - foreach ($attr->getUsedInObjectClasses() as $objectclass) { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['objectclasses'],strtolower($objectclass))); - if (isAjaxEnabled()) - printf('%s ', - $href,$href,_('ObjectClasses'),strtolower($objectclass),$objectclass); - else - printf('%s ',$href,$objectclass); - } - - echo '%s
'; - echo '
'; - echo '
'; - } - } - - break; - - case 'matching_rules': - $schema_matching_rules = $app['server']->MatchingRules(); - if (! $schema_matching_rules) - error($schema_error_str,'error','index.php'); - - printf('%s
',_('Jump to a matching rule')); - - echo '
'; - echo '
'; - echo ''; - printf('',$app['server']->getIndex()); - echo ''; - - if (isAjaxEnabled()) { - drawJSItems($schema_matching_rules); - echo ''; - - echo ''; - foreach ($schema_matching_rules as $rule) - printf('', - $rule->getName(), - ($rule->getName() == $entry['value'] ? 'selected="selected"': ''), - $rule->getName(false)); - - echo ''; - - if (isAjaxEnabled()) - printf('',_('Go')); - else - printf('',_('Go')); - echo '
'; - echo '
'; - echo '
'; - - echo ''; - printf('', - _('Matching Rule OID'),_('Name'),_('Used by Attributes')); - - $counter = 1; - - foreach ($schema_matching_rules as $rule) { - $counter++; - $oid = $rule->getOID(); - $desc = $rule->getName(false); - - if (isAjaxEnabled() || (is_null($entry['value']) || ! trim($entry['value']) || $entry['value']==$rule->getName())) { - if ((! is_null($entry['value']) && $entry['value']==$rule->getName()) || ! trim($entry['value'])) - $entry['viewed'] = true; - - if (null != $rule->getDescription()) - $desc .= sprintf(' (%s)',$rule->getDescription()); - - if ( $rule->getIsObsolete()) - $desc .= sprintf(' %s',_('Obsolete')); - - if (isAjaxEnabled() && $entry['value']) - printf('',$counter%2 ? 'odd' : 'even',$rule->getName(), - strcasecmp($entry['value'],$rule->getName()) ? 'none' : ''); - else - printf('',$counter%2 ? 'odd' : 'even',$rule->getName()); - printf('',$oid); - printf('',$desc); - - echo ''; - echo ''; - } - } - - echo '
%s%s%s
%s%s'; - - if (count($rule->getUsedByAttrs()) == 0) { - printf('
(%s)


',_('none')); - - } else { - echo '
'; - echo '
'; - echo '
'; - echo ''; - printf('',$app['server']->getIndex()); - echo ''; - - printf('
'; - - if (isAjaxEnabled()) - printf('', - _('Go'),$app['server']->getIndex(),_('Attributes'),$rule->getName()); - else - printf('',_('Go')); - echo '
'; - echo '
'; - echo '
'; - } - echo '
'; - break; - - case 'objectclasses': - $socs = $app['server']->SchemaObjectClasses(); - if (! $socs) - error($schema_error_str,'error','index.php'); - - printf('%s:',_('Jump to an objectClass')); - - echo '
'; - echo '
'; - echo ''; - printf('',$entry['view']); - printf('',$app['server']->getIndex()); - - if (isAjaxEnabled()) { - drawJSItems($socs); - echo ''; - - echo ''; - foreach ($socs as $name => $oclass) - printf('', - $name,$name == $entry['value'] ? 'selected="selected" ': '',$oclass->getName(false)); - - echo ''; - - if (isAjaxEnabled()) - printf('',_('Go')); - else - printf('',_('Go')); - echo '
'; - echo '
'; - echo '
'; - - foreach ($socs as $name => $oclass) { - if (isAjaxEnabled() || (is_null($entry['value']) || ! trim($entry['value']) || $entry['value']==$oclass->getName())) { - if ((! is_null($entry['value']) && $entry['value']==$oclass->getName()) || ! trim($entry['value'])) - $entry['viewed'] = true; - - if (isAjaxEnabled() && $entry['value']) - printf('
',$oclass->getName(),strcasecmp($entry['value'],$oclass->getName()) ? 'none' : ''); - else - printf('
',$oclass->getName()); - - echo ''; - printf('',$name,$oclass->getName(false)); - printf('',_('OID'),$oclass->getOID()); - - if ($oclass->getDescription()) - printf('',_('Description'),$oclass->getDescription()); - - printf('',_('Type'),$oclass->getType()); - - if ($oclass->getIsObsolete()) - printf('',_('This objectClass is obsolete.')); - - printf(''; - - printf(''; - - printf('', - _('Required Attributes'),_('Optional Attributes')); - - echo ''; - echo ''; - echo ''; - echo ''; - echo '
%s
%s: %s
%s: %s
%s: %s
%s
%s: ',_('Inherits from')); - if (count($oclass->getSupClasses()) == 0) - printf('(%s)',_('none')); - - else - foreach ($oclass->getSupClasses() as $i => $object_class) { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['objectclasses'],strtolower($object_class))); - if (isAjaxEnabled()) - printf('%s', - $href,strtolower($object_class),$object_class); - else - printf('%s', - $href,$object_class,_('Jump to this objectClass definition'),$object_class); - - if ($i < count($oclass->getSupClasses()) - 1) - echo ', '; - } - echo '
%s: ',_('Parent to')); - if (strcasecmp($oclass->getName(),'top') == 0) { - $href = htmlspecialchars($entry['href']['objectclasses']); - if (isAjaxEnabled()) - printf('all', - $href); - else - printf('(all)',$href); - - } elseif (count($oclass->getChildObjectClasses()) == 0) - printf('(%s)',_('none')); - - else - foreach ($oclass->getChildObjectClasses() as $i => $object_class) { - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['objectclasses'],strtolower($object_class))); - if (isAjaxEnabled()) - printf('%s', - $href,_('Jump to this objectClass definition'),strtolower($object_class),$object_class); - else - printf('%s',$href,_('Jump to this objectClass definition'),$object_class); - - if ( $i < count($oclass->getChildObjectClasses()) - 1) - echo ', '; - } - echo '
 %s%s 
'; - - if ($attrs = $oclass->getMustAttrs(true)) { - echo '
    '; - - foreach ($attrs as $attr) { - echo '
  • '; - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['attributes'],$attr->getName())); - if (isAjaxEnabled()) - printf('%s', - $href,$href,_('Attributes'),$attr->getName(),$attr->getName(false)); - else - printf('%s',$href,$attr->getName(false)); - - if ($attr->getSource() != $oclass->getName(false)) { - echo '
    '; - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['objectclasses'],strtolower($attr->getSource()))); - printf('(%s ',_('Inherited from')); - if (isAjaxEnabled()) - printf('%s', - $href,_('Jump to this objectClass definition'),strtolower($attr->getSource()),$attr->getSource()); - else - printf('%s',$href,$attr->getSource()); - echo ')'; - } - echo '
  • '; - } - echo '
'; - - } else - printf('(%s)',_('none')); - - echo '
'; - - if ($attrs = $oclass->getMayAttrs(true)) { - echo '
    '; - - foreach ($attrs as $attr) { - echo '
  • '; - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['attributes'],$attr->getName())); - if (isAjaxEnabled()) - printf('%s', - $href,$href,_('Attributes'),$attr->getName(),$attr->getName(false)); - else - printf('%s',$href,$attr->getName(false)); - - if ($attr->getSource() != $oclass->getName(false)) { - echo '
    '; - $href = htmlspecialchars(sprintf('%s&viewvalue=%s',$entry['href']['objectclasses'],strtolower($attr->getSource()))); - printf('(%s ',_('Inherited from')); - if (isAjaxEnabled()) - printf('%s', - $href,_('Jump to this objectClass definition'),strtolower($attr->getSource()),$attr->getSource()); - else - printf('%s',$href,$attr->getSource()); - echo ')'; - } - - if ($oclass->isForceMay($attr->getName())) { - echo '
    '; - printf('%s',_('This attribute has been forced as a MAY attribute by the configuration')); - } - echo '
  • '; - } - echo '
'; - - } else - printf('(%s)',_('none')); - - echo '
'; - echo '
'; - echo '
'; - } - } - break; -} - -if (! is_null($entry['value']) && ! $entry['viewed']) - error(sprintf(_('No such schema item: "%s"'),$entry['value']),'error','index.php'); - -function drawJSItems($object) { - echo ''; -} -?> diff --git a/lib/ds_ldap.php b/lib/ds_ldap.php index de80066..c312e7a 100644 --- a/lib/ds_ldap.php +++ b/lib/ds_ldap.php @@ -1134,859 +1134,7 @@ class ldap extends DS { } } - public function getRootDSE($method=null) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $query = array(); - $query['base'] = ''; - $query['scope'] = 'base'; - $query['attrs'] = $this->getValue('server','root_dse_attributes'); - $query['baseok'] = true; - $results = $this->query($query,$method); - - if (is_array($results) && count($results) == 1) - return array_change_key_case(array_pop($results)); - else - return array(); - } - /** Schema Methods **/ - /** - * This function will query the ldap server and request the subSchemaSubEntry which should be the Schema DN. - * - * If we cant connect to the LDAP server, we'll return false. - * If we can connect but cant get the entry, then we'll return null. - * - * @param string Which connection method resource to use - * @param dn The DN to use to obtain the schema - * @return array|false Schema if available, null if its not or false if we cant connect. - */ - private function getSchemaDN($method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # If we already got the SchemaDN, then return it. - if ($this->_schemaDN) - return $this->_schemaDN; - - if (! $this->connect($method)) - return false; - - $search = @ldap_read($this->connect($method),$dn,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER); - - if (DEBUG_ENABLED) - debug_log('Search returned (%s)',24,0,__FILE__,__LINE__,__METHOD__,is_resource($search)); - - # Fix for broken ldap.conf configuration. - if (! $search && ! $dn) { - if (DEBUG_ENABLED) - debug_log('Trying to find the DN for "broken" ldap.conf',80,0,__FILE__,__LINE__,__METHOD__); - - if (isset($this->_baseDN)) { - foreach ($this->_baseDN as $base) { - $search = @ldap_read($this->connect($method),$base,'objectclass=*',array('subschemaSubentry'),false,0,10,LDAP_DEREF_NEVER); - - if (DEBUG_ENABLED) - debug_log('Search returned (%s) for base (%s)',24,0,__FILE__,__LINE__,__METHOD__, - is_resource($search),$base); - - if ($search) - break; - } - } - } - - if (! $search) - return null; - - if (! @ldap_count_entries($this->connect($method),$search)) { - if (DEBUG_ENABLED) - debug_log('Search returned 0 entries. Returning NULL',25,0,__FILE__,__LINE__,__METHOD__); - - return null; - } - - $entries = @ldap_get_entries($this->connect($method),$search); - - if (DEBUG_ENABLED) - debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$entries); - - if (! $entries || ! is_array($entries)) - return null; - - $entry = isset($entries[0]) ? $entries[0] : false; - if (! $entry) { - if (DEBUG_ENABLED) - debug_log('Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__); - - return null; - } - - $sub_schema_sub_entry = isset($entry[0]) ? $entry[0] : false; - if (! $sub_schema_sub_entry) { - if (DEBUG_ENABLED) - debug_log('Sub Entry is false, Returning NULL',80,0,__FILE__,__LINE__,__METHOD__); - - return null; - } - - $this->_schemaDN = isset($entry[$sub_schema_sub_entry][0]) ? $entry[$sub_schema_sub_entry][0] : false; - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->_schemaDN); - - return $this->_schemaDN; - } - - /** - * Fetches the raw schema array for the subschemaSubentry of the server. Note, - * this function has grown many hairs to accomodate more LDAP servers. It is - * needfully complicated as it now supports many popular LDAP servers that - * don't necessarily expose their schema "the right way". - * - * Please note: On FC systems, it seems that php_ldap uses /etc/openldap/ldap.conf in - * the search base if it is blank - so edit that file and comment out the BASE line. - * - * @param string Which connection method resource to use - * @param string A string indicating which type of schema to - * fetch. Five valid values: 'objectclasses', 'attributetypes', - * 'ldapsyntaxes', 'matchingruleuse', or 'matchingrules'. - * Case insensitive. - * @param dn (optional) This paremeter is the DN of the entry whose schema you - * would like to fetch. Entries have the option of specifying - * their own subschemaSubentry that points to the DN of the system - * schema entry which applies to this attribute. If unspecified, - * this will try to retrieve the schema from the RootDSE subschemaSubentry. - * Failing that, we use some commonly known schema DNs. Default - * value is the Root DSE DN (zero-length string) - * @return array an array of strings of this form: - * Array ( - * [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ... - * [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ... - * etc. - */ - private function getRawSchema($method,$schema_to_fetch,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $valid_schema_to_fetch = array('objectclasses','attributetypes','ldapsyntaxes','matchingrules','matchingruleuse'); - - if (! $this->connect($method) || $this->noconnect) - return false; - - # error checking - $schema_to_fetch = strtolower($schema_to_fetch); - - if (! is_null($this->_schema_entries) && isset($this->_schema_entries[$schema_to_fetch])) { - $schema = $this->_schema_entries[$schema_to_fetch]; - - if (DEBUG_ENABLED) - debug_log('Returning CACHED (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema); - - return $schema; - } - - # This error message is not localized as only developers should ever see it - if (! in_array($schema_to_fetch,$valid_schema_to_fetch)) - error(sprintf('Bad parameter provided to function to %s::getRawSchema(). "%s" is not valid for the schema_to_fetch parameter.', - get_class($this),$schema_to_fetch),'error','index.php'); - - # Try to get the schema DN from the specified entry. - $schema_dn = $this->getSchemaDN($method,$dn); - - # Do we need to try again with the Root DSE? - if (! $schema_dn && trim($dn)) - $schema_dn = $this->getSchemaDN($method,''); - - # Store the eventual schema retrieval in $schema_search - $schema_search = null; - - if ($schema_dn) { - if (DEBUG_ENABLED) - debug_log('Using Schema DN (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_dn); - - foreach (array('(objectClass=*)','(objectClass=subschema)') as $schema_filter) { - if (DEBUG_ENABLED) - debug_log('Looking for schema with Filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter); - - $schema_search = @ldap_read($this->connect($method),$schema_dn,$schema_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); - - if (is_null($schema_search)) - continue; - - $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); - - if (DEBUG_ENABLED) - debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); - - if (is_array($schema_entries) && isset($schema_entries['count']) && $schema_entries['count']) { - if (DEBUG_ENABLED) - debug_log('Found schema with (DN:%s) (FILTER:%s) (ATTR:%s)',24,0,__FILE__,__LINE__,__METHOD__, - $schema_dn,$schema_filter,$schema_to_fetch); - - break; - } - - if (DEBUG_ENABLED) - debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$schema_filter); - - unset($schema_entries); - $schema_search = null; - } - } - - /* Second chance: If the DN or Root DSE didn't give us the subschemaSubentry, ie $schema_search - * is still null, use some common subSchemaSubentry DNs as a work-around. */ - if (is_null($schema_search)) { - if (DEBUG_ENABLED) - debug_log('Attempting work-arounds for "broken" LDAP servers...',24,0,__FILE__,__LINE__,__METHOD__); - - foreach ($this->getBaseDN() as $base) { - $ldap['W2K3 AD'][expand_dn_with_base($base,'cn=Aggregate,cn=Schema,cn=configuration,')] = '(objectClass=*)'; - $ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,cn=configuration,')] = '(objectClass=*)'; - $ldap['W2K AD'][expand_dn_with_base($base,'cn=Schema,ou=Admin,')] = '(objectClass=*)'; - } - - # OpenLDAP and Novell - $ldap['OpenLDAP']['cn=subschema'] = '(objectClass=*)'; - - foreach ($ldap as $ldap_server_name => $ldap_options) { - foreach ($ldap_options as $ldap_dn => $ldap_filter) { - if (DEBUG_ENABLED) - debug_log('Attempting [%s] (%s) (%s)
',24,0,__FILE__,__LINE__,__METHOD__, - $ldap_server_name,$ldap_dn,$ldap_filter); - - $schema_search = @ldap_read($this->connect($method),$ldap_dn,$ldap_filter,array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); - if (is_null($schema_search)) - continue; - - $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); - - if (DEBUG_ENABLED) - debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); - - if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) { - if (DEBUG_ENABLED) - debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter); - - break; - } - - if (DEBUG_ENABLED) - debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter); - - unset($schema_entries); - $schema_search = null; - } - if ($schema_search) - break; - } - } - - # Option 3: try cn=config - $olc_schema = 'olc'.$schema_to_fetch; - $olc_schema_found = false; - if (is_null($schema_search)) { - if (DEBUG_ENABLED) - debug_log('Attempting cn=config work-around...',24,0,__FILE__,__LINE__,__METHOD__); - - $ldap_dn = 'cn=schema,cn=config'; - $ldap_filter = '(objectClass=*)'; - - $schema_search = @ldap_search($this->connect($method),$ldap_dn,$ldap_filter,array($olc_schema),false,0,10,LDAP_DEREF_NEVER); - - if (! is_null($schema_search)) { - $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); - - if (DEBUG_ENABLED) - debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); - - if ($schema_entries) { - if (DEBUG_ENABLED) - debug_log('Found schema with filter of (%s) and attribute filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter,$olc_schema); - - $olc_schema_found = true; - - } else { - if (DEBUG_ENABLED) - debug_log('Didnt find schema with filter (%s) and attribute filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,$ldap_filter,$olc_schema); - - unset($schema_entries); - $schema_search = null; - } - } - } - - if (is_null($schema_search)) { - /* Still cant find the schema, try with the RootDSE - * Attempt to pull schema from Root DSE with scope "base", or - * Attempt to pull schema from Root DSE with scope "one" (work-around for Isode M-Vault X.500/LDAP) */ - foreach (array('base','one') as $ldap_scope) { - if (DEBUG_ENABLED) - debug_log('Attempting to find schema with scope (%s), filter (objectClass=*) and a blank base.',24,0,__FILE__,__LINE__,__METHOD__, - $ldap_scope); - - switch ($ldap_scope) { - case 'base': - $schema_search = @ldap_read($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); - break; - - case 'one': - $schema_search = @ldap_list($this->connect($method),'','(objectClass=*)',array($schema_to_fetch),false,0,10,LDAP_DEREF_NEVER); - break; - } - - if (is_null($schema_search)) - continue; - - $schema_entries = @ldap_get_entries($this->connect($method),$schema_search); - if (DEBUG_ENABLED) - debug_log('Search returned [%s]',24,0,__FILE__,__LINE__,__METHOD__,$schema_entries); - - if ($schema_entries && isset($schema_entries[0][$schema_to_fetch])) { - if (DEBUG_ENABLED) - debug_log('Found schema with filter of (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)'); - - break; - } - - if (DEBUG_ENABLED) - debug_log('Didnt find schema with filter (%s)',24,0,__FILE__,__LINE__,__METHOD__,'(objectClass=*)'); - - unset($schema_entries); - $schema_search = null; - } - } - - $schema_error_message = 'Please contact the phpLDAPadmin developers and let them know:
  • Which LDAP server you are running, including which version
  • What OS it is running on
  • Which version of PHP
  • As well as a link to some documentation that describes how to obtain the SCHEMA information

We\'ll then add support for your LDAP server in an upcoming release.'; - $schema_error_message_array = array('objectclasses','attributetypes'); - - # Shall we just give up? - if (is_null($schema_search)) { - # We need to have objectclasses and attribues, so display an error, asking the user to get us this information. - if (in_array($schema_to_fetch,$schema_error_message_array)) - system_message(array( - 'title'=>sprintf('%s (%s)',_('Our attempts to find your SCHEMA have failed'),$schema_to_fetch), - 'body'=>sprintf('%s: %s',_('Error'),$schema_error_message), - 'type'=>'error')); - else - if (DEBUG_ENABLED) - debug_log('Returning because schema_search is NULL ()',25,0,__FILE__,__LINE__,__METHOD__); - - # We'll set this, so if we return here our cache will return the known false. - $this->_schema_entries[$schema_to_fetch] = false; - return false; - } - - if (! $schema_entries) { - $return = false; - if (DEBUG_ENABLED) - debug_log('Returning false since ldap_get_entries() returned false.',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - if ($olc_schema_found) { - unset ($schema_entries['count']); - - foreach ($schema_entries as $entry) { - if (isset($entry[$olc_schema])) { - unset($entry[$olc_schema]['count']); - - foreach ($entry[$olc_schema] as $schema_definition) - /* Schema definitions in child nodes prefix the schema entries with "{n}" - the preg_replace call strips out this prefix. */ - $schema[] = preg_replace('/^\{\d*\}\(/','(',$schema_definition); - } - } - - if (isset($schema)) { - $this->_schema_entries[$olc_schema] = $schema; - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema); - - return $schema; - - } else - return null; - } - - if (! isset($schema_entries[0][$schema_to_fetch])) { - if (in_array($schema_to_fetch,$schema_error_message_array)) { - error(sprintf('Our attempts to find your SCHEMA for "%s" have return UNEXPECTED results.

(We expected a "%s" in the $schema array but it wasnt there.)

%s

Dump of $schema_search:
%s
', - $schema_to_fetch,gettype($schema_search),$schema_error_message,serialize($schema_entries)),'error','index.php'); - - } else { - $return = false; - - if (DEBUG_ENABLED) - debug_log('Returning because (%s) isnt in the schema array. (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema_to_fetch,$return); - - return $return; - } - } - - /* Make a nice array of this form: - Array ( - [0] => "(1.3.6.1.4.1.7165.1.2.2.4 NAME 'gidPool' DESC 'Pool ...)" - [1] => "(1.3.6.1.4.1.7165.2.2.3 NAME 'sambaAccount' DESC 'Sa ...)" - etc.) */ - - $schema = $schema_entries[0][$schema_to_fetch]; - unset($schema['count']); - $this->_schema_entries[$schema_to_fetch] = $schema; - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$schema); - - return $schema; - } - - /** - * Gets a single ObjectClass object specified by name. - * - * @param string $oclass_name The name of the objectClass to fetch. - * @param string $dn (optional) It is easier to fetch schema if a DN is provided - * which defines the subschemaSubEntry attribute (all entries should). - * - * @return ObjectClass The specified ObjectClass object or false on error. - * - * @see ObjectClass - * @see SchemaObjectClasses - */ - public function getSchemaObjectClass($oclass_name,$method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $oclass_name = strtolower($oclass_name); - $socs = $this->SchemaObjectClasses($method,$dn); - - # Default return value - $return = false; - - if (isset($socs[$oclass_name])) - $return = $socs[$oclass_name]; - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * Gets a single AttributeType object specified by name. - * - * @param string $oclass_name The name of the AttributeType to fetch. - * @param string $dn (optional) It is easier to fetch schema if a DN is provided - * which defines the subschemaSubEntry attribute (all entries should). - * - * @return AttributeType The specified AttributeType object or false on error. - * - * @see AttributeType - * @see SchemaAttributes - */ - public function getSchemaAttribute($attr_name,$method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $attr_name = strtolower($attr_name); - $sattrs = $this->SchemaAttributes($method,$dn); - - # Default return value - $return = false; - - if (isset($sattrs[$attr_name])) - $return = $sattrs[$attr_name]; - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * Gets an associative array of ObjectClass objects for the specified - * server. Each array entry's key is the name of the objectClass - * in lower-case and the value is an ObjectClass object. - * - * @param string $dn (optional) It is easier to fetch schema if a DN is provided - * which defines the subschemaSubEntry attribute (all entries should). - * - * @return array An array of ObjectClass objects. - * - * @see ObjectClass - * @see getSchemaObjectClass - */ - public function SchemaObjectClasses($method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # Set default return - $return = null; - - if ($return = get_cached_item($this->index,'schema','objectclasses')) { - if (DEBUG_ENABLED) - debug_log('Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'objectclasses'); - - return $return; - } - - $raw = $this->getRawSchema($method,'objectclasses',$dn); - - if ($raw) { - # Build the array of objectClasses - $return = array(); - - foreach ($raw as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $object_class = new ObjectClass($line,$this); - $return[$object_class->getName()] = $object_class; - } - - # Now go through and reference the parent/child relationships - foreach ($return as $oclass) - foreach ($oclass->getSupClasses() as $parent_name) - if (isset($return[strtolower($parent_name)])) - $return[strtolower($parent_name)]->addChildObjectClass($oclass->getName(false)); - - ksort($return); - - # cache the schema to prevent multiple schema fetches from LDAP server - set_cached_item($this->index,'schema','objectclasses',$return); - } - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * Gets an associative array of AttributeType objects for the specified - * server. Each array entry's key is the name of the attributeType - * in lower-case and the value is an AttributeType object. - * - * @param string $dn (optional) It is easier to fetch schema if a DN is provided - * which defines the subschemaSubEntry attribute (all entries should). - * - * @return array An array of AttributeType objects. - */ - public function SchemaAttributes($method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # Set default return - $return = null; - - if ($return = get_cached_item($this->index,'schema','attributes')) { - if (DEBUG_ENABLED) - debug_log('(): Returning CACHED [%s] (%s)',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'attributes'); - - return $return; - } - - $raw = $this->getRawSchema($method,'attributeTypes',$dn); - - if ($raw) { - # build the array of attribueTypes - $syntaxes = $this->SchemaSyntaxes($method,$dn); - $attrs = array(); - - /** - * bug 856832: create two arrays - one indexed by name (the standard - * $attrs array above) and one indexed by oid (the new $attrs_oid array - * below). This will help for directory servers, like IBM's, that use OIDs - * in their attribute definitions of SUP, etc - */ - $attrs_oid = array(); - foreach ($raw as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $attr = new AttributeType($line); - if (isset($syntaxes[$attr->getSyntaxOID()])) { - $syntax = $syntaxes[$attr->getSyntaxOID()]; - $attr->setType($syntax->getDescription()); - } - $attrs[$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 ($attrs as $name => $attr) { - $aliases = $attr->getAliases(); - - if (is_array($aliases) && count($aliases) > 0) { - /* 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.*/ - foreach ($aliases as $alias_attr_name) { - $new_attr = clone $attr; - - $new_attr->setName($alias_attr_name); - $new_attr->addAlias($attr->getName(false)); - $new_attr->removeAlias($alias_attr_name); - $new_attr_key = strtolower($alias_attr_name); - $attrs[$new_attr_key] = $new_attr; - } - } - } - - # go back and add any inherited descriptions from parent attributes (ie, cn inherits name) - foreach ($attrs as $key => $attr) { - $sup_attr_name = $attr->getSupAttribute(); - $sup_attr = null; - - if (trim($sup_attr_name)) { - - /* This loop really should traverse infinite levels of inheritance (SUP) for attributeTypes, - * but just in case we get carried away, stop at 100. This shouldn't happen, but for - * some weird reason, we have had someone report that it has happened. Oh well.*/ - $i = 0; - while ($i++<100 /** 100 == INFINITY ;) */) { - - if (isset($attrs_oid[$sup_attr_name])) { - $attr->setSupAttribute($attrs_oid[$sup_attr_name]->getName()); - $sup_attr_name = $attr->getSupAttribute(); - } - - if (! isset($attrs[strtolower($sup_attr_name)])){ - error(sprintf('Schema error: attributeType "%s" inherits from "%s", but attributeType "%s" does not exist.', - $attr->getName(),$sup_attr_name,$sup_attr_name),'error','index.php'); - return; - } - - $sup_attr = $attrs[strtolower($sup_attr_name)]; - $sup_attr_name = $sup_attr->getSupAttribute(); - - # Does this superior attributeType not have a superior attributeType? - if (is_null($sup_attr_name) || strlen(trim($sup_attr_name)) == 0) { - - /* Since this attribute's superior attribute does not have another superior - * attribute, clone its properties for this attribute. Then, replace - * those cloned values with those that can be explicitly set by the child - * attribute attr). Save those few properties which the child can set here:*/ - $tmp_name = $attr->getName(false); - $tmp_oid = $attr->getOID(); - $tmp_sup = $attr->getSupAttribute(); - $tmp_aliases = $attr->getAliases(); - $tmp_single_val = $attr->getIsSingleValue(); - $tmp_desc = $attr->getDescription(); - - /* clone the SUP attributeType and populate those values - * that were set by the child attributeType */ - $attr = clone $sup_attr; - - $attr->setOID($tmp_oid); - $attr->setName($tmp_name); - $attr->setSupAttribute($tmp_sup); - $attr->setAliases($tmp_aliases); - $attr->setDescription($tmp_desc); - - /* only overwrite the SINGLE-VALUE property if the child explicitly sets it - * (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */ - if ($tmp_single_val) - $attr->setIsSingleValue(true); - - /* replace this attribute in the attrs array now that we have populated - new values therein */ - $attrs[$key] = $attr; - - # very important: break out after we are done with this attribute - $sup_attr_name = null; - $sup_attr = null; - break; - } - } - } - } - - ksort($attrs); - - # Add the used in and required_by values. - $socs = $this->SchemaObjectClasses($method); - if (! is_array($socs)) - return array(); - - foreach ($socs as $object_class) { - $must_attrs = $object_class->getMustAttrNames(); - $may_attrs = $object_class->getMayAttrNames(); - $oclass_attrs = array_unique(array_merge($must_attrs,$may_attrs)); - - # Add Used In. - foreach ($oclass_attrs as $attr_name) - if (isset($attrs[strtolower($attr_name)])) - $attrs[strtolower($attr_name)]->addUsedInObjectClass($object_class->getName(false)); - - # Add Required By. - foreach ($must_attrs as $attr_name) - if (isset($attrs[strtolower($attr_name)])) - $attrs[strtolower($attr_name)]->addRequiredByObjectClass($object_class->getName(false)); - - # Force May - foreach ($object_class->getForceMayAttrs() as $attr_name) - if (isset($attrs[strtolower($attr_name->name)])) - $attrs[strtolower($attr_name->name)]->setForceMay(); - } - - $return = $attrs; - - # cache the schema to prevent multiple schema fetches from LDAP server - set_cached_item($this->index,'schema','attributes',$return); - } - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * Returns an array of MatchingRule objects for the specified server. - * The key of each entry is the OID of the matching rule. - */ - public function MatchingRules($method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # Set default return - $return = null; - - if ($return = get_cached_item($this->index,'schema','matchingrules')) { - if (DEBUG_ENABLED) - debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'matchingrules'); - - return $return; - } - - # build the array of MatchingRule objects - $raw = $this->getRawSchema($method,'matchingRules',$dn); - - if ($raw) { - $rules = array(); - - foreach ($raw as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $rule = new MatchingRule($line); - $key = $rule->getName(); - $rules[$key] = $rule; - } - - ksort($rules); - - /* For each MatchingRuleUse entry, add the attributes who use it to the - * MatchingRule in the $rules array.*/ - $raw = $this->getRawSchema($method,'matchingRuleUse'); - - if ($raw != false) { - foreach ($raw as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $rule_use = new MatchingRuleUse($line); - $key = $rule_use->getName(); - - if (isset($rules[$key])) - $rules[$key]->setUsedByAttrs($rule_use->getUsedByAttrs()); - } - - } else { - /* No MatchingRuleUse entry in the subschema, so brute-forcing - * the reverse-map for the "$rule->getUsedByAttrs()" data.*/ - $sattrs = $this->SchemaAttributes($method,$dn); - if (is_array($sattrs)) - foreach ($sattrs as $attr) { - $rule_key = strtolower($attr->getEquality()); - - if (isset($rules[$rule_key])) - $rules[$rule_key]->addUsedByAttr($attr->getName(false)); - } - } - - $return = $rules; - - # cache the schema to prevent multiple schema fetches from LDAP server - set_cached_item($this->index,'schema','matchingrules',$return); - } - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * Returns an array of Syntax objects that this LDAP server uses mapped to - * their descriptions. The key of each entry is the OID of the Syntax. - */ - public function SchemaSyntaxes($method=null,$dn='') { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',25,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # Set default return - $return = null; - - if ($return = get_cached_item($this->index,'schema','syntaxes')) { - if (DEBUG_ENABLED) - debug_log('Returning CACHED [%s] (%s).',25,0,__FILE__,__LINE__,__METHOD__,$this->index,'syntaxes'); - - return $return; - } - - $raw = $this->getRawSchema($method,'ldapSyntaxes',$dn); - - if ($raw) { - # build the array of attributes - $return = array(); - - foreach ($raw as $line) { - if (is_null($line) || ! strlen($line)) - continue; - - $syntax = new Syntax($line); - $key = strtolower(trim($syntax->getOID())); - - if (! $key) - continue; - - $return[$key] = $syntax; - } - - ksort($return); - - # cache the schema to prevent multiple schema fetches from LDAP server - set_cached_item($this->index,'schema','syntaxes',$return); - } - - if (DEBUG_ENABLED) - debug_log('Returning (%s)',25,0,__FILE__,__LINE__,__METHOD__,$return); - - return $return; - } - - /** - * This function determines if the specified attribute is contained in the force_may list - * as configured in config.php. - * - * @return boolean True if the specified attribute is configured to be force as a may attribute - */ - function isForceMay($attr_name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',17,0,__FILE__,__LINE__,__METHOD__,$fargs); - - return in_array($attr_name,unserialize(strtolower(serialize($this->getValue('server','force_may'))))); - } /** * Much like getDNAttrValues(), but only returns the values for diff --git a/lib/schema_functions.php b/lib/schema_functions.php deleted file mode 100644 index efa0cbc..0000000 --- a/lib/schema_functions.php +++ /dev/null @@ -1,1539 +0,0 @@ -oid = $oid; - } - - public function setDescription($desc) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->description = $desc; - } - - public function getOID() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->oid); - - return $this->oid; - } - - public function getDescription() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); - - return $this->description; - } - - /** - * Gets whether this objectClass is flagged as obsolete by the LDAP server. - */ - public function getIsObsolete() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_obsolete); - - return $this->is_obsolete; - } - - /** - * Return the objects name. - * - * param boolean $lower Return the name in lower case (default) - * @return string The name - */ - public function getName($lower=true) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); - - return $lower ? strtolower($this->name) : $this->name; - } -} - -/** - * Represents an LDAP objectClass - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class ObjectClass extends SchemaItem { - # The server ID that this objectclass belongs to. - private $server_id = null; - # Array of objectClass names from which this objectClass inherits - private $sup_classes = array(); - # One of STRUCTURAL, ABSTRACT, or AUXILIARY - private $type; - # Arrays of attribute names that this objectClass requires - private $must_attrs = array(); - # Arrays of attribute names that this objectClass allows, but does not require - private $may_attrs = array(); - # Arrays of attribute names that this objectClass has been forced to MAY attrs, due to configuration - private $force_may = array(); - # Array of objectClasses which inherit from this one (must be set at runtime explicitly by the caller) - private $children_objectclasses = array(); - # The objectclass hierarchy - private $hierarchy = array(); - - /** - * Creates a new ObjectClass object given a raw LDAP objectClass string. - */ - public function __construct($class,$server) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->server_id = $server->getIndex(); - $this->type = $server->getValue('server','schema_oclass_default'); - - $strings = preg_split('/[\s,]+/',$class,-1,PREG_SPLIT_DELIM_CAPTURE); - $str_count = count($strings); - - for ($i=0; $i < $str_count; $i++) { - - switch ($strings[$i]) { - case '(': - break; - - case 'NAME': - if ($strings[$i+1]!='(') { - do { - $i++; - if (strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - - } while (! preg_match('/\'$/s',$strings[$i])); - - } else { - $i++; - do { - $i++; - if (strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - - } while (! preg_match('/\'$/s',$strings[$i])); - - do { - $i++; - } while (! preg_match('/\)+\)?/',$strings[$i])); - } - - $this->name = preg_replace('/^\'/','',$this->name); - $this->name = preg_replace('/\'$/','',$this->name); - - if (DEBUG_ENABLED) - debug_log('Case NAME returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->name); - break; - - case 'DESC': - do { - $i++; - if (strlen($this->description) == 0) - $this->description=$this->description.$strings[$i]; - else - $this->description=$this->description.' '.$strings[$i]; - - } while (! preg_match('/\'$/s',$strings[$i])); - - if (DEBUG_ENABLED) - debug_log('Case DESC returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->description); - break; - - case 'OBSOLETE': - $this->is_obsolete = TRUE; - - if (DEBUG_ENABLED) - debug_log('Case OBSOLETE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_obsolete); - break; - - case 'SUP': - if ($strings[$i+1] != '(') { - $i++; - array_push($this->sup_classes,preg_replace("/'/",'',$strings[$i])); - - } else { - $i++; - do { - $i++; - if ($strings[$i] != '$') - array_push($this->sup_classes,preg_replace("/'/",'',$strings[$i])); - - } while (! preg_match('/\)+\)?/',$strings[$i+1])); - } - - if (DEBUG_ENABLED) - debug_log('Case SUP returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sup_classes); - break; - - case 'ABSTRACT': - $this->type = 'abstract'; - - if (DEBUG_ENABLED) - debug_log('Case ABSTRACT returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); - break; - - case 'STRUCTURAL': - $this->type = 'structural'; - - if (DEBUG_ENABLED) - debug_log('Case STRUCTURAL returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); - break; - - case 'AUXILIARY': - $this->type = 'auxiliary'; - - if (DEBUG_ENABLED) - debug_log('Case AUXILIARY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->type); - break; - - case 'MUST': - $attrs = array(); - - $i = $this->parseList(++$i,$strings,$attrs); - - if (DEBUG_ENABLED) - debug_log('parseList returned %d (%s)',8,0,__FILE__,__LINE__,__METHOD__,$i,$attrs); - - foreach ($attrs as $string) { - $attr = new ObjectClass_ObjectClassAttribute($string,$this->name); - - if ($server->isForceMay($attr->getName())) { - array_push($this->force_may,$attr); - array_push($this->may_attrs,$attr); - - } else - array_push($this->must_attrs,$attr); - } - - if (DEBUG_ENABLED) - debug_log('Case MUST returned (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->must_attrs,$this->force_may); - break; - - case 'MAY': - $attrs = array(); - - $i = $this->parseList(++$i,$strings,$attrs); - - if (DEBUG_ENABLED) - debug_log('parseList returned %d (%s)',8,0,__FILE__,__LINE__,__METHOD__,$i,$attrs); - - foreach ($attrs as $string) { - $attr = new ObjectClass_ObjectClassAttribute($string,$this->name); - array_push($this->may_attrs,$attr); - } - - if (DEBUG_ENABLED) - debug_log('Case MAY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->may_attrs); - break; - - default: - if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) { - $this->setOID($strings[$i]); - - if (DEBUG_ENABLED) - debug_log('Case default returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->getOID()); - } - break; - } - } - - $this->description = preg_replace("/^\'/",'',$this->description); - $this->description = preg_replace("/\'$/",'',$this->description); - - if (DEBUG_ENABLED) - debug_log('Returning () - NAME (%s), DESCRIPTION (%s), MUST (%s), MAY (%s), FORCE MAY (%s)',9,0,__FILE__,__LINE__,__METHOD__, - $this->name,$this->description,$this->must_attrs,$this->may_attrs,$this->force_may); - } - - /** - * Parse an LDAP schema list - */ - private function parseList($i,$strings,&$attrs) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - /* - * A list starts with a ( followed by a list of attributes separated by $ terminated by ) - * The first token can therefore be a ( or a (NAME or a (NAME) - * The last token can therefore be a ) or NAME) - * The last token may be terminate by more than one bracket - */ - - $string = $strings[$i]; - if (! preg_match('/^\(/',$string)) { - # A bareword only - can be terminated by a ) if the last item - if (preg_match('/\)+$/',$string)) - $string = preg_replace('/\)+$/','',$string); - - array_push($attrs,$string); - - } elseif (preg_match('/^\(.*\)$/',$string)) { - $string = preg_replace('/^\(/','',$string); - $string = preg_replace('/\)+$/','',$string); - array_push($attrs,$string); - - } else { - # Handle the opening cases first - if ($string == '(') { - $i++; - - } elseif (preg_match('/^\(./',$string)) { - $string = preg_replace('/^\(/','',$string); - array_push($attrs,$string); - $i++; - } - - # Token is either a name, a $ or a ')' - # NAME can be terminated by one or more ')' - while (! preg_match('/\)+$/',$strings[$i])) { - $string = $strings[$i]; - if ($string == '$') { - $i++; - continue; - } - - if (preg_match('/\)$/',$string)) - $string = preg_replace('/\)+$/','',$string); - else - $i++; - - array_push($attrs,$string); - } - } - - sort($attrs); - - if (DEBUG_ENABLED) - debug_log('Returning (%d,[%s],[%s])',9,0,__FILE__,__LINE__,__METHOD__,$i,$strings,$attrs); - - return $i; - } - - /** - * This will return all our parent ObjectClass Objects - */ - public function getParents() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if ((count($this->sup_classes) == 1) && ($this->sup_classes[0] == 'top')) - return array(); - - $server = $_SESSION[APPCONFIG]->getServer($this->server_id); - $return = array(); - - foreach ($this->sup_classes as $object_class) { - array_push($return,$object_class); - - $oc = $server->getSchemaObjectClass($object_class); - - if ($oc) - $return = array_merge($return,$oc->getParents()); - } - - return $return; - } - - /** - * Gets an array of AttributeType objects that entries of this ObjectClass must define. - * This differs from getMustAttrNames in that it returns an array of AttributeType objects - * - * @param array $parents An array of ObjectClass objects to use when traversing - * the inheritance tree. This presents some what of a bootstrapping problem - * as we must fetch all objectClasses to determine through inheritance which - * attributes this objectClass requires. - * @return array The array of required AttributeType objects. - * - * @see getMustAttrNames - * @see getMayAttrs - * @see getMayAttrNames - */ - public function getMustAttrs($parents=false) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if (! $parents) - return $this->must_attrs; - - $server = $_SESSION[APPCONFIG]->getServer($this->server_id); - $attrs = $this->must_attrs; - - foreach ($this->getParents() as $sup_class) { - $sc = $server->getSchemaObjectClass($sup_class); - $attrs = array_merge($attrs,$sc->getMustAttrs()); - } - - masort($attrs,'name,source'); - - # Remove any duplicates - foreach ($attrs as $index => $attr) - if (isset($allattr[$attr->getName()])) - unset($attrs[$index]); - else - $allattr[$attr->getName()] = 1; - - return $attrs; - } - - /** - * Gets an array of AttributeType objects that entries of this ObjectClass may define. - * This differs from getMayAttrNames in that it returns an array of AttributeType objects - * - * @param array $parents An array of ObjectClass objects to use when traversing - * the inheritance tree. This presents some what of a bootstrapping problem - * as we must fetch all objectClasses to determine through inheritance which - * attributes this objectClass provides. - * @return array The array of allowed AttributeType objects. - * - * @see getMustAttrNames - * @see getMustAttrs - * @see getMayAttrNames - * @see AttributeType - */ - public function getMayAttrs($parents=false) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if (! $parents) - return $this->may_attrs; - - $server = $_SESSION[APPCONFIG]->getServer($this->server_id); - $attrs = $this->may_attrs; - - foreach ($this->getParents() as $sup_class) { - $sc = $server->getSchemaObjectClass($sup_class); - $attrs = array_merge($attrs,$sc->getMayAttrs()); - } - - masort($attrs,'name,source'); - - # Remove any duplicates - foreach ($attrs as $index => $attr) - if (isset($allattr[$attr->name])) - unset($attrs[$index]); - else - $allattr[$attr->name] = 1; - - return $attrs; - } - - public function getForceMayAttrs() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - return $this->force_may; - } - - /** - * Gets an array of attribute names (strings) that entries of this ObjectClass must define. - * This differs from getMustAttrs in that it returns an array of strings rather than - * array of AttributeType objects - * - * @param array $parents An array of ObjectClass objects to use when traversing - * the inheritance tree. This presents some what of a bootstrapping problem - * as we must fetch all objectClasses to determine through inheritance which - * attributes this objectClass provides. - * @return array The array of allowed attribute names (strings). - * - * @see getMustAttrs - * @see getMayAttrs - * @see getMayAttrNames - */ - public function getMustAttrNames($parents=false) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $attr_names = array(); - - foreach ($this->getMustAttrs($parents) as $attr) - array_push($attr_names,$attr->getName()); - - return $attr_names; - } - - /** - * Gets an array of attribute names (strings) that entries of this ObjectClass must define. - * This differs from getMayAttrs in that it returns an array of strings rather than - * array of AttributeType objects - * - * @param array $parents An array of ObjectClass objects to use when traversing - * the inheritance tree. This presents some what of a bootstrapping problem - * as we must fetch all objectClasses to determine through inheritance which - * attributes this objectClass provides. - * @return array The array of allowed attribute names (strings). - * - * @see getMustAttrs - * @see getMayAttrs - * @see getMustAttrNames - */ - public function getMayAttrNames($parents=false) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $attr_names = array(); - - foreach ($this->getMayAttrs($parents) as $attr) - array_push($attr_names,$attr->getName()); - - return $attr_names; - } - - /** - * Adds an objectClass to the list of objectClasses that inherit - * from this objectClass. - * - * @param String $name The name of the objectClass to add - * @return boolean Returns true on success or false on failure (objectclass already existed for example) - */ - public function addChildObjectClass($name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $name = trim($name); - - foreach ($this->children_objectclasses as $existing_objectclass) - if (strcasecmp($name,$existing_objectclass) == 0) - return false; - - array_push($this->children_objectclasses,$name); - } - - /** - * Returns the array of objectClass names which inherit from this objectClass. - * - * @return Array Names of objectClasses which inherit from this objectClass. - */ - public function getChildObjectClasses() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - return $this->children_objectclasses; - } - - /** - * Gets the objectClass names from which this objectClass inherits. - * - * @return array An array of objectClass names (strings) - */ - public function getSupClasses() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - return $this->sup_classes; - } - - /** - * Return if this objectClass is related to $oclass - * - * @param array ObjectClasses that this attribute may be related to - */ - public function isRelated($oclass) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - # If I am in the array, we'll just return false - if (in_array_ignore_case($this->name,$oclass)) - return false; - - $server = $_SESSION[APPCONFIG]->getServer($this->server_id); - - foreach ($oclass as $object_class) { - $oc = $server->getSchemaObjectClass($object_class); - - if ($oc->isStructural() && in_array_ignore_case($this->getName(),$oc->getParents())) - return true; - } - - return false; - } - - /** - * Gets the type of this objectClass: STRUCTURAL, ABSTRACT, or AUXILIARY. - */ - public function getType() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); - - return $this->type; - } - - /** - * Adds the specified array of attributes to this objectClass' list of - * MUST attributes. The resulting array of must attributes will contain - * unique members. - * - * @param array $attr An array of attribute names (strings) to add. - */ - private function addMustAttrs($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if (! is_array($attr) || ! count($attr)) - return; - - $this->must_attrs = array_values(array_unique(array_merge($this->must_attrs,$attr))); - } - - /** - * Behaves identically to addMustAttrs, but it operates on the MAY - * attributes of this objectClass. - * - * @param array $attr An array of attribute names (strings) to add. - */ - private function addMayAttrs($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if (! is_array($attr) || ! count($attr)) - return; - - $this->may_attrs = array_values(array_unique(array_merge($this->may_attrs,$attr))); - } - - /** - * Determine if an array is listed in the force_may attrs - */ - public function isForceMay($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->force_may as $forcemay) - if ($forcemay->getName() == $attr) - return true; - - return false; - } - - public function isStructural() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - if ($this->type == 'structural') - return true; - else - return false; - } -} - -/** - * A simple class for representing AttributeTypes used only by the ObjectClass class. - * Users should never instantiate this class. It represents an attribute internal to - * an ObjectClass. If PHP supported inner-classes and variable permissions, this would - * be interior to class ObjectClass and flagged private. The reason this class is used - * and not the "real" class AttributeType is because this class supports the notion of - * a "source" objectClass, meaning that it keeps track of which objectClass originally - * specified it. This class is therefore used by the class ObjectClass to determine - * inheritance. - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class ObjectClass_ObjectClassAttribute { - # This Attribute's name (needs to be public, as we sort on it with masort). - public $name; - # This Attribute's root (needs to be public, as we sort on it with masort). - public $source; - - /** - * Creates a new ObjectClass_ObjectClassAttribute with specified name and source objectClass. - * - * @param string $name the name of the new attribute. - * @param string $source the name of the ObjectClass which specifies this attribute. - */ - public function __construct($name,$source) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->name = $name; - $this->source = $source; - } - - # Gets this attribute's name - public function getName($lower=true) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->name); - - return $lower ? strtolower($this->name) : $this->name; - } - - # Gets the name of the ObjectClass which originally specified this attribute. - public function getSource() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->source); - - return $this->source; - } -} - -/** - * Represents an LDAP AttributeType - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class AttributeType extends SchemaItem { - # The attribute from which this attribute inherits (if any) - private $sup_attribute = null; - # The equality rule used - private $equality = null; - # The ordering of the attributeType - private $ordering = null; - # Boolean: supports substring matching? - private $sub_str = null; - # The full syntax string, ie 1.2.3.4{16} - private $syntax = null; - private $syntax_oid = null; - # boolean: is single valued only? - private $is_single_value = false; - # boolean: is collective? - private $is_collective = false; - # boolean: can use modify? - private $is_no_user_modification = false; - # The usage string set by the LDAP schema - private $usage = null; - # An array of alias attribute names, strings - private $aliases = array(); - # The max number of characters this attribute can be - private $max_length = null; - # A string description of the syntax type (taken from the LDAPSyntaxes) - private $type = null; - # An array of objectClasses which use this attributeType (must be set by caller) - private $used_in_object_classes = array(); - # A list of object class names that require this attribute type. - private $required_by_object_classes = array(); - # This attribute has been forced a MAY attribute by the configuration. - private $forced_as_may = false; - - /** - * Creates a new AttributeType object from a raw LDAP AttributeType string. - */ - public function __construct($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $strings = preg_split('/[\s,]+/',$attr,-1,PREG_SPLIT_DELIM_CAPTURE); - - for($i=0; $iname)==0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - - } while (! preg_match("/\'$/s",$strings[$i])); - - # This attribute has no aliases - $this->aliases = array(); - - } else { - $i++; - do { - # In case we came here becaues of a (' - if (preg_match('/^\(/',$strings[$i])) - $strings[$i] = preg_replace('/^\(/','',$strings[$i]); - else - $i++; - - if (strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - - } while (! preg_match("/\'$/s",$strings[$i])); - - # Add alias names for this attribute - while ($strings[++$i] != ')') { - $alias = $strings[$i]; - $alias = preg_replace("/^\'/",'',$alias); - $alias = preg_replace("/\'$/",'',$alias); - $this->addAlias($alias); - } - } - - if (DEBUG_ENABLED) - debug_log('Case NAME returned (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->name,$this->aliases); - break; - - case 'DESC': - do { - $i++; - if (strlen($this->description)==0) - $this->description=$this->description.$strings[$i]; - else - $this->description=$this->description.' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - - if (DEBUG_ENABLED) - debug_log('Case DESC returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->description); - break; - - case 'OBSOLETE': - $this->is_obsolete = TRUE; - - if (DEBUG_ENABLED) - debug_log('Case OBSOLETE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_obsolete); - break; - - case 'SUP': - $i++; - $this->sup_attribute = $strings[$i]; - - if (DEBUG_ENABLED) - debug_log('Case SUP returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sup_attribute); - break; - - case 'EQUALITY': - $i++; - $this->equality = $strings[$i]; - - if (DEBUG_ENABLED) - debug_log('Case EQUALITY returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->equality); - break; - - case 'ORDERING': - $i++; - $this->ordering = $strings[$i]; - - if (DEBUG_ENABLED) - debug_log('Case ORDERING returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->ordering); - break; - - case 'SUBSTR': - $i++; - $this->sub_str = $strings[$i]; - - if (DEBUG_ENABLED) - debug_log('Case SUBSTR returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->sub_str); - break; - - case 'SYNTAX': - $i++; - $this->syntax = $strings[$i]; - $this->syntax_oid = preg_replace('/{\d+}$/','',$this->syntax); - - # Does this SYNTAX string specify a max length (ie, 1.2.3.4{16}) - if (preg_match('/{(\d+)}$/',$this->syntax,$this->max_length)) - $this->max_length = $this->max_length[1]; - else - $this->max_length = null; - - if ($i < count($strings) - 1 && $strings[$i+1] == '{') { - do { - $i++; - $this->name .= ' '.$strings[$i]; - } while ($strings[$i] != '}'); - } - - if (DEBUG_ENABLED) - debug_log('Case SYNTAX returned (%s) (%s) (%s)',8,0,__FILE__,__LINE__,__METHOD__, - $this->syntax,$this->syntax_oid,$this->max_length); - break; - - case 'SINGLE-VALUE': - $this->is_single_value = TRUE; - if (DEBUG_ENABLED) - debug_log('Case SINGLE-VALUE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_single_value); - break; - - case 'COLLECTIVE': - $this->is_collective = TRUE; - - if (DEBUG_ENABLED) - debug_log('Case COLLECTIVE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_collective); - break; - - case 'NO-USER-MODIFICATION': - $this->is_no_user_modification = TRUE; - - if (DEBUG_ENABLED) - debug_log('Case NO-USER-MODIFICATION returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->is_no_user_modification); - break; - - case 'USAGE': - $i++; - $this->usage = $strings[$i]; - - if (DEBUG_ENABLED) - debug_log('Case USAGE returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->usage); - break; - - default: - if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) { - $this->setOID($strings[$i]); - - if (DEBUG_ENABLED) - debug_log('Case default returned (%s)',8,0,__FILE__,__LINE__,__METHOD__,$this->getOID()); - } - } - } - - $this->name = preg_replace("/^\'/",'',$this->name); - $this->name = preg_replace("/\'$/",'',$this->name); - $this->description = preg_replace("/^\'/",'',$this->description); - $this->description = preg_replace("/\'$/",'',$this->description); - $this->syntax = preg_replace("/^\'/",'',$this->syntax); - $this->syntax = preg_replace("/\'$/",'',$this->syntax); - $this->syntax_oid = preg_replace("/^\'/",'',$this->syntax_oid); - $this->syntax_oid = preg_replace("/\'$/",'',$this->syntax_oid); - $this->sup_attribute = preg_replace("/^\'/",'',$this->sup_attribute); - $this->sup_attribute = preg_replace("/\'$/",'',$this->sup_attribute); - - if (DEBUG_ENABLED) - debug_log('Returning ()',9,0,__FILE__,__LINE__,__METHOD__); - } - - /** - * Gets this attribute's usage string as defined by the LDAP server - * - * @return string - */ - public function getUsage() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->usage); - - return $this->usage; - } - - /** - * Gets this attribute's parent attribute (if any). If this attribute does not - * inherit from another attribute, null is returned. - * - * @return string - */ - public function getSupAttribute() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->sup_attribute); - - return $this->sup_attribute; - } - - /** - * Gets this attribute's equality string - * - * @return string - */ - public function getEquality() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->equality); - - return $this->equality; - } - - /** - * Gets this attribute's ordering specification. - * - * @return string - */ - public function getOrdering() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->ordering); - - return $this->ordering; - } - - /** - * Gets this attribute's substring matching specification - * - * @return string - */ - public function getSubstr() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->sub_str); - - return $this->sub_str; - } - - /** - * Gets the names of attributes that are an alias for this attribute (if any). - * - * @return array An array of names of attributes which alias this attribute or - * an empty array if no attribute aliases this object. - */ - public function getAliases() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->aliases); - - return $this->aliases; - } - - /** - * Returns whether the specified attribute is an alias for this one (based on this attribute's alias list). - * - * @param string $attr_name The name of the attribute to check. - * @return boolean True if the specified attribute is an alias for this one, or false otherwise. - */ - public function isAliasFor($attr_name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->aliases as $alias_attr_name) - if (strcasecmp($alias_attr_name,$attr_name) == 0) - return true; - - return false; - } - - /** - * Gets this attribute's raw syntax string (ie: "1.2.3.4{16}"). - * - * @return string The raw syntax string - */ - public function getSyntaxString() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax); - - return $this->syntax; - } - - /** - * Gets this attribute's syntax OID. Differs from getSyntaxString() in that this - * function only returns the actual OID with any length specification removed. - * Ie, if the syntax string is "1.2.3.4{16}", this function only retruns - * "1.2.3.4". - * - * @return string The syntax OID string. - */ - public function getSyntaxOID() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->syntax_oid); - - return $this->syntax_oid; - } - - /** - * Gets this attribute's the maximum length. If no maximum is defined by the LDAP server, null is returned. - * - * @return int The maximum length (in characters) of this attribute or null if no maximum is specified. - */ - public function getMaxLength() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->max_length); - - return $this->max_length; - } - - /** - * Gets whether this attribute is single-valued. If this attribute only supports single values, true - * is returned. If this attribute supports multiple values, false is returned. - * - * @return boolean Returns true if this attribute is single-valued or false otherwise. - */ - public function getIsSingleValue() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_single_value); - - return $this->is_single_value; - } - - /** - * Sets whether this attribute is single-valued. - * - * @param boolean $is - */ - public function setIsSingleValue($is) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->is_single_value = $is; - } - - /** - * Gets whether this attribute is collective. - * - * @return boolean Returns true if this attribute is collective and false otherwise. - */ - public function getIsCollective() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_collective); - - return $this->is_collective; - } - - /** - * Gets whether this attribute is not modifiable by users. - * - * @return boolean Returns true if this attribute is not modifiable by users. - */ - public function getIsNoUserModification() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->is_no_user_modification); - - return $this->is_no_user_modification; - } - - /** - * Gets this attribute's type - * - * @return string The attribute's type. - */ - public function getType() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->type); - - return $this->type; - } - - /** - * Removes an attribute name from this attribute's alias array. - * - * @param string $remove_alias_name The name of the attribute to remove. - * @return boolean true on success or false on failure (ie, if the specified - * attribute name is not found in this attribute's list of aliases) - */ - public function removeAlias($remove_alias_name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->aliases as $i => $alias_name) { - - if (strcasecmp($alias_name,$remove_alias_name) == 0) { - unset($this->aliases[$i]); - - $this->aliases = array_values($this->aliases); - return true; - } - } - return false; - } - - /** - * Adds an attribute name to the alias array. - * - * @param string $alias The name of a new attribute to add to this attribute's list of aliases. - */ - public function addAlias($alias) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - array_push($this->aliases,$alias); - } - - /** - * Sets this attriute's name. - * - * @param string $name The new name to give this attribute. - */ - public function setName($name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->name = $name; - } - - /** - * Sets this attriute's SUP attribute (ie, the attribute from which this attribute inherits). - * - * @param string $attr The name of the new parent (SUP) attribute - */ - public function setSupAttribute($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->sup_attribute = $attr; - } - - /** - * Sets this attribute's list of aliases. - * - * @param array $aliases The array of alias names (strings) - */ - public function setAliases($aliases) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->aliases = $aliases; - } - - /** - * Sets this attribute's type. - * - * @param string $type The new type. - */ - public function setType($type) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->type = $type; - } - - /** - * Adds an objectClass name to this attribute's list of "used in" objectClasses, - * that is the list of objectClasses which provide this attribute. - * - * @param string $name The name of the objectClass to add. - */ - public function addUsedInObjectClass($name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->used_in_object_classes as $used_in_object_class) { - if (DEBUG_ENABLED) - debug_log('Checking (%s) with (%s)',8,0,__FILE__,__LINE__,__METHOD__,$used_in_object_class,$name); - - if (strcasecmp($used_in_object_class,$name) == 0) - return false; - } - - array_push($this->used_in_object_classes,$name); - } - - /** - * Gets the list of "used in" objectClasses, that is the list of objectClasses - * which provide this attribute. - * - * @return array An array of names of objectclasses (strings) which provide this attribute - */ - public function getUsedInObjectClasses() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_in_object_classes); - - return $this->used_in_object_classes; - } - - /** - * Adds an objectClass name to this attribute's list of "required by" objectClasses, - * that is the list of objectClasses which must have this attribute. - * - * @param string $name The name of the objectClass to add. - */ - public function addRequiredByObjectClass($name) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->required_by_object_classes as $required_by_object_class) - if (strcasecmp($required_by_object_class,$name) == 0) - return false; - - array_push($this->required_by_object_classes,$name); - } - - /** - * Gets the list of "required by" objectClasses, that is the list of objectClasses - * which provide must have attribute. - * - * @return array An array of names of objectclasses (strings) which provide this attribute - */ - public function getRequiredByObjectClasses() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->required_by_object_classes); - - return $this->required_by_object_classes; - } - - /** - * This function will mark this attribute as a forced MAY attribute - */ - public function setForceMay() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->forced_as_may = true; - } - - public function isForceMay() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->forced_as_may); - - return $this->forced_as_may; - } -} - -/** - * Represents an LDAP Syntax - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class Syntax extends SchemaItem { - /** - * Creates a new Syntax object from a raw LDAP syntax string. - */ - public function __construct($class) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $strings = preg_split('/[\s,]+/',$class,-1,PREG_SPLIT_DELIM_CAPTURE); - - for($i=0; $idescription) == 0) - $this->description=$this->description.$strings[$i]; - else - $this->description=$this->description.' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - break; - - default: - if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) - $this->setOID($strings[$i]); - } - } - - $this->description = preg_replace("/^\'/",'',$this->description); - $this->description = preg_replace("/\'$/",'',$this->description); - } -} - -/** - * Represents an LDAP MatchingRule - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class MatchingRule extends SchemaItem { - # This rule's syntax OID - private $syntax = null; - # An array of attribute names who use this MatchingRule - private $used_by_attrs = array(); - - /** - * Creates a new MatchingRule object from a raw LDAP MatchingRule string. - */ - function __construct($strings) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $strings = preg_split('/[\s,]+/',$strings,-1,PREG_SPLIT_DELIM_CAPTURE); - - for ($i=0; $iname) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - - } else { - $i++; - do { - $i++; - if (strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - - do { - $i++; - } while (! preg_match('/\)+\)?/',$strings[$i])); - } - - $this->name = preg_replace("/^\'/",'',$this->name); - $this->name = preg_replace("/\'$/",'',$this->name); - break; - - case 'DESC': - do { - $i++; - if (strlen($this->description)==0) - $this->description=$this->description.$strings[$i]; - else - $this->description=$this->description.' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - break; - - case 'OBSOLETE': - $this->is_obsolete = TRUE; - break; - - case 'SYNTAX': - $this->syntax = $strings[++$i]; - break; - - default: - if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) - $this->setOID($strings[$i]); - } - } - $this->description = preg_replace("/^\'/",'',$this->description); - $this->description = preg_replace("/\'$/",'',$this->description); - } - - /** - * Sets the list of used_by_attrs to the array specified by $attrs; - * - * @param array $attrs The array of attribute names (strings) which use this MatchingRule - */ - public function setUsedByAttrs($attrs) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs); - - $this->used_by_attrs = $attrs; - } - - /** - * Adds an attribute name to the list of attributes who use this MatchingRule - * - * @return true if the attribute was added and false otherwise (already in the list) - */ - public function addUsedByAttr($attr) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - foreach ($this->used_by_attrs as $attr_name) - if (strcasecmp($attr_name,$attr) == 0) - return false; - - array_push($this->used_by_attrs,$attr); - - return true; - } - - /** - * Gets an array of attribute names (strings) which use this MatchingRule - * - * @return array The array of attribute names (strings). - */ - public function getUsedByAttrs() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_by_attrs); - - return $this->used_by_attrs; - } -} - -/** - * Represents an LDAP schema matchingRuleUse entry - * - * @package phpLDAPadmin - * @subpackage Schema - */ -class MatchingRuleUse extends SchemaItem { - # An array of attribute names who use this MatchingRule - private $used_by_attrs = array(); - - function __construct($strings) { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,0,__FILE__,__LINE__,__METHOD__,$fargs); - - $strings = preg_split('/[\s,]+/',$strings,-1,PREG_SPLIT_DELIM_CAPTURE); - - for($i=0; $iname) || strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - - } while (! preg_match("/\'$/s",$strings[$i])); - - } else { - $i++; - do { - $i++; - if (strlen($this->name) == 0) - $this->name = $strings[$i]; - else - $this->name .= ' '.$strings[$i]; - } while (! preg_match("/\'$/s",$strings[$i])); - - do { - $i++; - } while (! preg_match('/\)+\)?/',$strings[$i])); - } - - $this->name = preg_replace("/^\'/",'',$this->name); - $this->name = preg_replace("/\'$/",'',$this->name); - break; - - case 'APPLIES': - if ($strings[$i+1] != '(') { - # Has a single attribute name - $i++; - $this->used_by_attrs = array($strings[$i]); - - } else { - # Has multiple attribute names - $i++; - while ($strings[$i] != ')') { - $i++; - $new_attr = $strings[$i]; - $new_attr = preg_replace("/^\'/",'',$new_attr); - $new_attr = preg_replace("/\'$/",'',$new_attr); - array_push($this->used_by_attrs,$new_attr); - $i++; - } - } - break; - - default: - if (preg_match('/[\d\.]+/i',$strings[$i]) && $i == 1) - $this->setOID($strings[$i]); - } - } - - sort($this->used_by_attrs); - } - - /** - * Gets an array of attribute names (strings) which use this MatchingRuleUse object. - * - * @return array The array of attribute names (strings). - */ - public function getUsedByAttrs() { - if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) - debug_log('Entered (%%)',9,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->used_by_attrs); - - return $this->used_by_attrs; - } -} -?> diff --git a/resources/themes/architect/views/layouts/partials/contentheader.blade.php b/resources/themes/architect/views/layouts/partials/contentheader.blade.php index 46f456c..41565bb 100644 --- a/resources/themes/architect/views/layouts/partials/contentheader.blade.php +++ b/resources/themes/architect/views/layouts/partials/contentheader.blade.php @@ -18,13 +18,13 @@
{{-- --}} -
\ No newline at end of file +
+ +@yield('page-scripts') \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 40af372..27db59d 100644 --- a/routes/api.php +++ b/routes/api.php @@ -16,8 +16,9 @@ use App\Http\Controllers\APIController; */ Route::group([],function() { - Route::get('/bases',[APIController::class,'bases']); - Route::get('/children',[APIController::class,'children']); + Route::get('bases',[APIController::class,'bases']); + Route::get('children',[APIController::class,'children']); + Route::post('schema/view',[APIController::class,'schema_view']); }); Route::group(['middleware'=>'auth:api','prefix'=>'user'],function() { diff --git a/routes/web.php b/routes/web.php index 8508c34..4d2a47e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -30,6 +30,7 @@ Route::group(['prefix' => LaravelLocalization::setLocale()], function() { Route::get('info',[HomeController::class,'info']); Route::post('dn',[HomeController::class,'dn_frame']); Route::get('debug',[HomeController::class,'debug']); + Route::get('schema',[HomeController::class,'schema_frame']); }); Route::get('logout',[LoginController::class,'logout']);