diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php index 3816c6b..2c9fc40 100644 --- a/app/Classes/LDAP/Attribute.php +++ b/app/Classes/LDAP/Attribute.php @@ -33,12 +33,16 @@ class Attribute // Is this attribute the RDN? protected bool $is_rdn = FALSE; + // Objectclasses that require this attribute protected Collection $required_by; // MIN/MAX number of values protected int $min_values_count = 0; protected int $max_values_count = 0; + // RFC3866 Language Tags + protected Collection $lang_tags; + /* # Has the attribute been modified protected $modified = false; @@ -94,6 +98,7 @@ class Attribute { $this->name = $name; $this->values = collect($values); + $this->lang_tags = collect(); // No need to load our schema for internal attributes if (! $this->is_internal) @@ -183,6 +188,10 @@ class Attribute if ($this->required_by->count()) $result->put(__('required'),sprintf('%s: %s',__('Required Attribute by ObjectClass(es)'),$this->required_by->join(','))); + // This attribute has language tags + if ($this->lang_tags->count()) + $result->put(__('language tags'),sprintf('%s: %d',__('This Attribute has Language Tags'),$this->lang_tags->count())); + return $result->toArray(); } @@ -199,6 +208,18 @@ class Attribute : collect()); } + /** + * If this attribute has RFC3866 Language Tags, this will enable those values to be captured + * + * @param string $tag + * @param array $value + * @return void + */ + public function setLangTag(string $tag,array $value): void + { + $this->lang_tags->put($tag,$value); + } + public function setRDN(): void { $this->is_rdn = TRUE; diff --git a/app/Classes/LDAP/Server.php b/app/Classes/LDAP/Server.php index 365017c..a9bb4bf 100644 --- a/app/Classes/LDAP/Server.php +++ b/app/Classes/LDAP/Server.php @@ -266,6 +266,18 @@ class Server return in_array($attr_name,config('pla.force_may',[])); } + /** + * Does this server support RFC3666 language tags + * OID: 1.3.6.1.4.1.4203.1.5.4 + * + * @return bool + * @throws ObjectNotFoundException + */ + public function isLanguageTags(): bool + { + return in_array('1.3.6.1.4.1.4203.1.5.4',$this->rootDSE()->supportedfeatures); + } + /** * Return the server's schema * @@ -513,6 +525,13 @@ class Server return is_null($key) ? $result : $result->get($key); } + /** + * Given an OID, return the ldapsyntax for the OID + * + * @param string $oid + * @return LDAPSyntax|null + * @throws InvalidUsage + */ public function schemaSyntaxName(string $oid): ?LDAPSyntax { return $this->schema('ldapsyntaxes',$oid); diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index ef3118d..eca3366 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -21,16 +21,29 @@ class Entry extends Model $result = collect(); foreach (parent::getAttributes() as $attribute => $value) { - $o = Factory::create($attribute,$value); + // If the attribute name has language tags + $matches = []; + if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) { + $attribute = $matches[1]; - // Set the rdn flag - if (preg_match('/^'.$attribute.'=/i',$this->dn)) - $o->setRDN(); + // If the attribute doesnt exist we'll create it + $o = Arr::get($result,$attribute,Factory::create($attribute,[])); + $o->setLangTag($matches[3],$value); - // Set required flag - $o->required_by(collect($this->getAttribute('objectclass'))); + } else { + $o = Factory::create($attribute,$value); + } - $result->put($attribute,$o); + if (! $result->has($attribute)) { + // Set the rdn flag + if (preg_match('/^'.$attribute.'=/i',$this->dn)) + $o->setRDN(); + + // Set required flag + $o->required_by(collect($this->getAttribute('objectclass'))); + + $result->put($attribute,$o); + } } $sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); }); diff --git a/tests/server/openldap/data/10-example.com.ldif b/tests/server/openldap/data/10-example.com.ldif index 043136a..bd1ceb4 100644 --- a/tests/server/openldap/data/10-example.com.ldif +++ b/tests/server/openldap/data/10-example.com.ldif @@ -18,12 +18,20 @@ objectclass: dNSDomain dn: c=AU,dc=example,dc=com c: AU description: Australia +description: Ozzieland +description;lang-fr: Australie +description;lang-en: Australia +description;lang-en: New Holland +description;lang-en;lang-aussie: Stralia +description;lang-cn: 澳大利亚 +description;lang-jp: オーストラリア objectclass: country objectclass: top # Entry 3: c=CN,dc=example,dc=com dn: c=CN,dc=example,dc=com c: CN +description;lang-cn:中国 objectclass: country objectclass: top @@ -51,6 +59,7 @@ objectclass: top dn: c=FR,dc=example,dc=com c: FR description: France +description;lang-fr: Le français est parlé ici objectclass: country objectclass: top