From d326d3c308c9f50675a9c995a14f35c76bf6e1ea Mon Sep 17 00:00:00 2001 From: Deon George Date: Fri, 14 Mar 2025 16:54:10 +1100 Subject: [PATCH] Store our DN and objectclasses in Attribute::class entries, so that we can dynamically calculate is_rdn and required objects (to be implemented) --- app/Classes/LDAP/Attribute.php | 16 ++++++++- app/Classes/LDAP/Attribute/Factory.php | 6 ++-- app/Classes/LDAP/Attribute/ObjectClass.php | 12 +++++-- app/Classes/LDAP/Import.php | 30 ++++++++++------ app/Classes/LDAP/Import/LDIF.php | 6 ++-- app/Http/Controllers/HomeController.php | 15 ++++---- app/Ldap/Entry.php | 40 +++++++++++++--------- 7 files changed, 82 insertions(+), 43 deletions(-) diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php index 225f99a1..f1002662 100644 --- a/app/Classes/LDAP/Attribute.php +++ b/app/Classes/LDAP/Attribute.php @@ -34,10 +34,14 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator // The schema's representation of this attribute protected(set) ?AttributeType $schema; + // The DN this object is in + protected(set) string $dn; // The old values for this attribute - helps with isDirty() to determine if there is an update pending protected(set) Collection $values_old; // Current Values public Collection $values; + // The other object classes of the entry that include this attribute + protected(set) Collection $oc; /* # Has the attribute been modified @@ -90,12 +94,22 @@ class Attribute implements \Countable, \ArrayAccess, \Iterator protected $postvalue = array(); */ - public function __construct(string $name,array $values) + /** + * Create an Attribute + * + * @param string $dn DN this attribute is used in + * @param string $name Name of the attribute + * @param array $values Current Values + * @param array $oc ObjectClasses that the DN has, that includes this attribute + */ + public function __construct(string $dn,string $name,array $values,array $oc=[]) { + $this->dn = $dn; $this->name = $name; $this->values_old = collect($values); $this->values = collect(); + $this->oc = collect($oc); $this->lang_tags = collect(); $this->schema = (new Server) diff --git a/app/Classes/LDAP/Attribute/Factory.php b/app/Classes/LDAP/Attribute/Factory.php index 4598607e..e11ee1f8 100644 --- a/app/Classes/LDAP/Attribute/Factory.php +++ b/app/Classes/LDAP/Attribute/Factory.php @@ -49,15 +49,17 @@ class Factory /** * Create the new Object for an attribute * + * @param string $dn * @param string $attribute * @param array $values + * @param array $oc * @return Attribute */ - public static function create(string $attribute,array $values): Attribute + public static function create(string $dn,string $attribute,array $values,array $oc=[]): Attribute { $class = Arr::get(self::map,strtolower($attribute),Attribute::class); Log::debug(sprintf('%s:Creating LDAP Attribute [%s] as [%s]',static::LOGKEY,$attribute,$class)); - return new $class($attribute,$values); + return new $class($dn,$attribute,$values,$oc); } } \ No newline at end of file diff --git a/app/Classes/LDAP/Attribute/ObjectClass.php b/app/Classes/LDAP/Attribute/ObjectClass.php index 8ae69ada..2667b0be 100644 --- a/app/Classes/LDAP/Attribute/ObjectClass.php +++ b/app/Classes/LDAP/Attribute/ObjectClass.php @@ -15,9 +15,17 @@ final class ObjectClass extends Attribute // The schema ObjectClasses for this objectclass of a DN protected Collection $oc_schema; - public function __construct(string $name,array $values) + /** + * Create an ObjectClass Attribute + * + * @param string $dn DN this attribute is used in + * @param string $name Name of the attribute + * @param array $values Current Values + * @param array $oc ObjectClasses that the DN has, that includes this attribute + */ + public function __construct(string $dn,string $name,array $values,array $oc=[]) { - parent::__construct($name,$values); + parent::__construct($dn,$name,$values,$oc); $this->oc_schema = config('server') ->schema('objectclasses') diff --git a/app/Classes/LDAP/Import.php b/app/Classes/LDAP/Import.php index 3340c185..9008f3fe 100644 --- a/app/Classes/LDAP/Import.php +++ b/app/Classes/LDAP/Import.php @@ -3,9 +3,9 @@ namespace App\Classes\LDAP; use Illuminate\Support\Collection; +use LdapRecord\LdapRecordException; use App\Exceptions\Import\GeneralException; -use App\Exceptions\Import\ObjectExistsException; use App\Ldap\Entry; /** @@ -48,7 +48,6 @@ abstract class Import * @param int $action * @return Collection * @throws GeneralException - * @throws ObjectExistsException */ final protected function commit(Entry $o,int $action): Collection { @@ -57,15 +56,24 @@ abstract class Import try { $o->save(); - } catch (\Exception $e) { - return collect([ - 'dn'=>$o->getDN(), - 'result'=>sprintf('%d: %s (%s)', - ($x=$e->getDetailedError())->getErrorCode(), - $x->getErrorMessage(), - $x->getDiagnosticMessage(), - ) - ]); + } catch (LdapRecordException $e) { + if ($e->getDetailedError()) + return collect([ + 'dn'=>$o->getDN(), + 'result'=>sprintf('%d: %s (%s)', + ($x=$e->getDetailedError())->getErrorCode(), + $x->getErrorMessage(), + $x->getDiagnosticMessage(), + ) + ]); + else + return collect([ + 'dn'=>$o->getDN(), + 'result'=>sprintf('%d: %s', + $e->getCode(), + $e->getMessage(), + ) + ]); } return collect(['dn'=>$o->getDN(),'result'=>__('Created')]); diff --git a/app/Classes/LDAP/Import/LDIF.php b/app/Classes/LDAP/Import/LDIF.php index 96261898..eb2c3408 100644 --- a/app/Classes/LDAP/Import/LDIF.php +++ b/app/Classes/LDAP/Import/LDIF.php @@ -46,7 +46,7 @@ class LDIF extends Import if (! $line) { if (! is_null($o)) { // Add the last attribute; - $o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value); + $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN())); @@ -125,7 +125,7 @@ class LDIF extends Import Log::debug(sprintf('%s: Adding Attribute [%s] value [%s] (%d)',self::LOGKEY,$attribute,$value,$c)); if ($value) - $o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value); + $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); else throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c)); } @@ -147,7 +147,7 @@ class LDIF extends Import // We may still have a pending action if ($action) { // Add the last attribute; - $o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value); + $o->addAttributeItem($attribute,$base64encoded ? base64_decode($value) : $value); Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN())); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index e808252f..b79458f3 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -69,7 +69,7 @@ class HomeController extends Controller $o->objectclass = $x; foreach($o->getAvailableAttributes()->filter(fn($item)=>$item->required) as $ao) - $o->addAttribute($ao,''); + $o->{$ao->name} = ''; $o->setRDNBase($key['dn']); } @@ -96,14 +96,14 @@ class HomeController extends Controller $xx = new \stdClass; $xx->index = 0; - $x = $request->noheader + $dn = $request->dn ? Crypt::decrypt($request->dn) : ''; + + return $request->noheader ? view(sprintf('components.attribute.widget.%s',$id)) - ->with('o',Factory::create($id,[])) + ->with('o',Factory::create($dn,$id,[],$request->oc ?: [])) ->with('value',$request->value) ->with('loop',$xx) - : (new AttributeType(Factory::create($id,[]),TRUE,collect($request->oc ?: [])))->render(); - - return $x; + : (new AttributeType(Factory::create($dn,$id,[],$request->oc ?: []),TRUE,collect($request->oc ?: [])))->render(); } public function entry_create(EntryAddRequest $request): \Illuminate\Http\RedirectResponse @@ -214,7 +214,8 @@ class HomeController extends Controller */ public function entry_objectclass_add(Request $request): Collection { - $oc = Factory::create('objectclass',$request->oc); + $dn = $request->key ? Crypt::decryptString($request->dn) : ''; + $oc = Factory::create($dn,'objectclass',$request->oc); $ocs = $oc ->structural diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index 212504a5..937efcd5 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -82,22 +82,22 @@ class Entry extends Model /** * As attribute values are updated, or new ones created, we need to mirror that - * into our $objects + * into our $objects. This is called when we $o->key = $value * * @param string $key * @param mixed $value * @return $this */ - public function setAttribute(string $key, mixed $value): static + public function setAttribute(string $key,mixed $value): static { parent::setAttribute($key,$value); $key = $this->normalizeAttributeKey($key); - if ((! $this->objects->get($key)) && $value) - $this->objects->put($key,Factory::create($key,[])); + $o = $this->objects->get($key) ?: Factory::create($this->dn ?: '',$key,[],Arr::get($this->attributes,'objectclass',[])); + $o->values = collect($this->attributes[$key]); - $this->objects->get($key)->values = collect($this->attributes[$key]); + $this->objects->put($key,$o); return $this; } @@ -140,7 +140,18 @@ class Entry extends Model /* METHODS */ - public function addAttribute(string $key,mixed $value): void + /** + * Add an attribute to this entry, if the attribute already exists, then we'll add the value to the existing item. + * + * This is primarily used by LDIF imports, where attributes have multiple entries over multiple lines + * + * @param string $key + * @param mixed $value + * @return void + * @throws AttributeException + * @note Attributes added this way dont have objectclass information, and the Model::attributes are not populated + */ + public function addAttributeItem(string $key,mixed $value): void { // While $value is mixed, it can only be a string if (! is_string($value)) @@ -151,15 +162,10 @@ class Entry extends Model if (! config('server')->schema('attributetypes')->has($key)) throw new AttributeException(sprintf('Schema doesnt have attribute [%s]',$key)); - if ($x=$this->objects->get($key)) { - $x->addValue($value); + $o = $this->objects->get($key) ?: Attribute\Factory::create($this->dn ?: '',$key,[]); + $o->addValue($value); - } else { - $o = Attribute\Factory::create($key,[]); - $o->values = collect(Arr::wrap($value)); - - $this->objects->put($key,$o); - } + $this->objects->put($key,$o); } /** @@ -178,11 +184,11 @@ class Entry extends Model $attribute = $matches[1]; // If the attribute doesnt exist we'll create it - $o = Arr::get($result,$attribute,Factory::create($attribute,Arr::get($this->original,$attribute,[]))); + $o = Arr::get($result,$attribute,Factory::create($this->dn,$attribute,Arr::get($this->original,$attribute,[]),Arr::get($this->original,'objectclass',[]))); $o->setLangTag($matches[3],$values); } else { - $o = Factory::create($attribute,Arr::get($this->original,$attribute,[])); + $o = Factory::create($this->dn,$attribute,Arr::get($this->original,$attribute,[]),Arr::get($this->original,'objectclass',[])); } if (! $result->has($attribute)) { @@ -299,7 +305,7 @@ class Entry extends Model private function getRDNObject(): Attribute\RDN { - $o = new Attribute\RDN('dn',['']); + $o = new Attribute\RDN('','dn',['']); // @todo for an existing object, return the base. $o->setBase($this->rdnbase); $o->setAttributes($this->getAvailableAttributes()->filter(fn($item)=>$item->required));