+ {!! ($x=$s->schema('attributetypes',$attribute)) + ? sprintf('%s',$x->name_lc,url('schema/attributetypes',$x->name_lc),$x->name) + : $attribute !!} + | +
+ |
+
---|
diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php
index 5a185bc..e636a0c 100644
--- a/app/Classes/LDAP/Attribute.php
+++ b/app/Classes/LDAP/Attribute.php
@@ -168,6 +168,11 @@ class Attribute implements \Countable, \ArrayAccess
return $this->name;
}
+ public function addValue(string $value): void
+ {
+ $this->values->push($value);
+ }
+
public function count(): int
{
return $this->values->count();
diff --git a/app/Classes/LDAP/Attribute/Factory.php b/app/Classes/LDAP/Attribute/Factory.php
index a45b8ea..08856a6 100644
--- a/app/Classes/LDAP/Attribute/Factory.php
+++ b/app/Classes/LDAP/Attribute/Factory.php
@@ -50,7 +50,7 @@ class Factory
*/
public static function create(string $attribute,array $values): Attribute
{
- $class = Arr::get(self::map,$attribute,Attribute::class);
+ $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);
diff --git a/app/Classes/LDAP/Export.php b/app/Classes/LDAP/Export.php
new file mode 100644
index 0000000..1abd1b9
--- /dev/null
+++ b/app/Classes/LDAP/Export.php
@@ -0,0 +1,53 @@
+items = $items;
+ }
+
+ abstract public function __toString(): string;
+
+ protected function header()
+ {
+ $output = '';
+
+ $output .= sprintf('# %s %s',__(static::type.' for'),($x=$this->items->first())).$this->br;
+ $output .= sprintf('# %s: %s (%s)',
+ __('Server'),
+ $x->getConnection()->getConfiguration()->get('name'),
+ $x->getConnection()->getLdapConnection()->getHost()).$this->br;
+ //$output .= sprintf('# %s: %s',__('Search Scope'),$this->scope).$this->br;
+ //$output .= sprintf('# %s: %s',__('Search Filter'),$this->entry->dn).$this->br;
+ $output .= sprintf('# %s: %s',__('Total Entries'),$this->items->count()).$this->br;
+ $output .= '#'.$this->br;
+ $output .= sprintf('# %s %s (%s) on %s',__('Generated by'),config('app.name'),config('app.url'),date('F j, Y g:i a')).$this->br;
+ $output .= sprintf('# %s %s',__('Exported by'),Auth::user() ?: 'Anonymous').$this->br;
+ $output .= sprintf('# %s: %s',__('Version'),config('app.version')).$this->br;
+
+ $output .= $this->br;
+
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/LDAP/Export/LDIF.php b/app/Classes/LDAP/Export/LDIF.php
new file mode 100644
index 0000000..873f37f
--- /dev/null
+++ b/app/Classes/LDAP/Export/LDIF.php
@@ -0,0 +1,78 @@
+br;
+
+ $c = 1;
+ foreach ($this->items as $o) {
+ if ($c > 1)
+ $result .= $this->br;
+
+ $title = (string)$o;
+ if (strlen($title) > $this->line_length)
+ $title = Str::of($title)->limit($this->line_length-3-5,'...'.substr($title,-5));
+
+ $result .= sprintf('# %s %s: %s',__('Entry'),$c++,$title).$this->br;
+
+ // Display DN
+ $result .= $this->multiLineDisplay(
+ Str::isAscii($o)
+ ? sprintf('dn: %s',$o)
+ : sprintf('dn:: %s',base64_encode($o))
+ ,$this->br);
+
+ // Display Attributes
+ foreach ($o->getObjects() as $ao) {
+ foreach ($ao->values as $value) {
+ $result .= $this->multiLineDisplay(
+ Str::isAscii($value)
+ ? sprintf('%s: %s',$ao->name,$value)
+ : sprintf('%s:: %s',$ao->name,base64_encode($value))
+ ,$this->br);
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Helper method to wrap LDIF lines
+ *
+ * @param string $str The line to be wrapped if needed.
+ */
+ private function multiLineDisplay(string $str,string $br): string
+ {
+ $length_string = strlen($str);
+ $length_max = $this->line_length;
+
+ $output = '';
+ while ($length_string > $length_max) {
+ $output .= substr($str,0,$length_max).$br;
+ $str = ' '.substr($str,$length_max);
+ $length_string = strlen($str);
+ }
+
+ $output .= $str.$br;
+
+ return $output;
+ }
+}
\ No newline at end of file
diff --git a/app/Classes/LDAP/Import.php b/app/Classes/LDAP/Import.php
new file mode 100644
index 0000000..3340c18
--- /dev/null
+++ b/app/Classes/LDAP/Import.php
@@ -0,0 +1,79 @@
+ self::LDAP_IMPORT_ADD,
+ 'delete' => self::LDAP_IMPORT_DELETE,
+ 'modrdn' => self::LDAP_IMPORT_MODRDN,
+ 'moddn' => self::LDAP_IMPORT_MODDN,
+ 'modify' => self::LDAP_IMPORT_MODIFY,
+ ];
+
+ // The import data to process
+ protected string $input;
+ // The attributes the server knows about
+ protected Collection $server_attributes;
+
+ public function __construct(string $input) {
+ $this->input = $input;
+ $this->server_attributes = config('server')->schema('attributetypes');
+ }
+
+ /**
+ * Attempt to commit an entry and return the result.
+ *
+ * @param Entry $o
+ * @param int $action
+ * @return Collection
+ * @throws GeneralException
+ * @throws ObjectExistsException
+ */
+ final protected function commit(Entry $o,int $action): Collection
+ {
+ switch ($action) {
+ case static::LDAP_IMPORT_ADD:
+ try {
+ $o->save();
+
+ } catch (\Exception $e) {
+ return collect([
+ 'dn'=>$o->getDN(),
+ 'result'=>sprintf('%d: %s (%s)',
+ ($x=$e->getDetailedError())->getErrorCode(),
+ $x->getErrorMessage(),
+ $x->getDiagnosticMessage(),
+ )
+ ]);
+ }
+
+ return collect(['dn'=>$o->getDN(),'result'=>__('Created')]);
+
+ default:
+ throw new GeneralException('Unhandled action during commit: '.$action);
+ }
+ }
+
+ abstract public function process(): Collection;
+}
\ No newline at end of file
diff --git a/app/Classes/LDAP/Import/LDIF.php b/app/Classes/LDAP/Import/LDIF.php
new file mode 100644
index 0000000..9626189
--- /dev/null
+++ b/app/Classes/LDAP/Import/LDIF.php
@@ -0,0 +1,233 @@
+input) as $line) {
+ $c++;
+ Log::debug(sprintf('%s: LDIF Line [%s]',self::LOGKEY,$line));
+ $line = trim($line);
+
+ // If the line starts with a comment, ignore it
+ if (preg_match('/^#/',$line))
+ continue;
+
+ // If we have a blank line, then that completes this command
+ if (! $line) {
+ if (! is_null($o)) {
+ // Add the last attribute;
+ $o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
+
+ Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
+
+ // Commit
+ $result->push($this->commit($o,$action));
+ $result->last()->put('line',$c);
+
+ $o = NULL;
+ $action = NULL;
+ $base64encoded = FALSE;
+ $attribute = NULL;
+ $value = '';
+
+ // Else its a blank line
+ }
+
+ continue;
+ }
+
+ $m = [];
+ preg_match('/^([a-zA-Z0-9;-]+)(:+)\s+(.*)$/',$line,$m);
+
+ switch ($x=Arr::get($m,1)) {
+ case 'changetype':
+ if ($m[2] !== ':')
+ throw new GeneralException(sprintf('ChangeType cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
+
+ switch ($m[3]) {
+ // if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
+ default:
+ throw new NotImplementedException(sprintf('Unknown change type [%s]? (line %d)',$m[3],$c));
+ }
+
+ break;
+
+ case 'version':
+ if (! is_null($version))
+ throw new VersionException(sprintf('Version has already been set at [%d]. (line %d)',$version,$c));
+
+ if ($m[2] !== ':')
+ throw new VersionException(sprintf('Version cannot be base64 encoded set at [%d]. (line %d)',$version,$c));
+
+ $version = (int)$m[3];
+ break;
+
+ // Treat it as an attribute
+ default:
+ // If $m is NULL, then this is the 2nd (or more) line of a base64 encoded value
+ if (! $m) {
+ $value .= $line;
+ Log::debug(sprintf('%s: Attribute [%s] adding [%s] (%d)',self::LOGKEY,$attribute,$line,$c));
+
+ // add to last attr value
+ continue 2;
+ }
+
+ // We are ready to create the entry or add the attribute
+ if ($attribute) {
+ if ($attribute === 'dn') {
+ if (! is_null($o))
+ throw new GeneralException(sprintf('Previous Entry not complete? (line %d)',$c));
+
+ $dn = $base64encoded ? base64_decode($value) : $value;
+ Log::debug(sprintf('%s: Creating new entry:',self::LOGKEY,$dn));
+ //$o = Entry::find($dn);
+
+ // If it doesnt exist, we'll create it
+ //if (! $o) {
+ $o = new Entry;
+ $o->setDn($dn);
+ //}
+
+ $action = self::LDAP_IMPORT_ADD;
+
+ } else {
+ 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);
+ else
+ throw new GeneralException(sprintf('Attribute has no value [%s] (line %d)',$attribute,$c));
+ }
+ }
+
+ // Start of a new attribute
+ $base64encoded = ($m[2] === '::');
+ // @todo Need to parse attributes with ';' options
+ $attribute = $m[1];
+ $value = $m[3];
+
+ Log::debug(sprintf('%s: New Attribute [%s] with [%s] (%d)',self::LOGKEY,$attribute,$value,$c));
+ }
+
+ if ($version !== 1)
+ throw new VersionException('LDIF import cannot handle version: '.($version ?: __('NOT DEFINED')));
+ }
+
+ // We may still have a pending action
+ if ($action) {
+ // Add the last attribute;
+ $o->addAttribute($attribute,$base64encoded ? base64_decode($value) : $value);
+
+ Log::debug(sprintf('%s: Committing Entry [%s]',self::LOGKEY,$o->getDN()));
+
+ // Commit
+ $result->push($this->commit($o,$action));
+ $result->last()->put('line',$c);
+ }
+
+ return $result;
+ }
+
+ public function readEntry() {
+ static $haveVersion = false;
+
+ if ($lines = $this->nextLines()) {
+
+ $server = $this->getServer();
+
+ # The first line should be the DN
+ if (preg_match('/^dn:/',$lines[0])) {
+ list($text,$dn) = $this->getAttrValue(array_shift($lines));
+
+ # The second line should be our changetype
+ if (preg_match('/^changetype:[ ]*(delete|add|modrdn|moddn|modify)/i',$lines[0])) {
+ $attrvalue = $this->getAttrValue($lines[0]);
+ $changetype = $attrvalue[1];
+ array_shift($lines);
+
+ } else
+ $changetype = 'add';
+
+ $this->template = new Template($this->server_id,null,null,$changetype);
+
+ switch ($changetype) {
+ case 'add':
+ $rdn = get_rdn($dn);
+ $container = $server->getContainer($dn);
+
+ $this->template->setContainer($container);
+ $this->template->accept();
+
+ $this->getAddDetails($lines);
+ $this->template->setRDNAttributes($rdn);
+
+ return $this->template;
+
+ break;
+
+ case 'modify':
+ if (! $server->dnExists($dn))
+ return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
+
+ $this->template->setDN($dn);
+ $this->template->accept(false,true);
+
+ return $this->getModifyDetails($lines);
+
+ break;
+
+ case 'moddn':
+ case 'modrdn':
+ if (! $server->dnExists($dn))
+ return $this->error(sprintf('%s %s',_('DN does not exist'),$dn),$lines);
+
+ $this->template->setDN($dn);
+ $this->template->accept();
+
+ return $this->getModRDNAttributes($lines);
+
+ break;
+
+ default:
+ if (! $server->dnExists($dn))
+ return $this->error(_('Unkown change type'),$lines);
+ }
+
+ } else
+ return $this->error(_('A valid dn line is required'),$lines);
+
+ } else
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/app/Exceptions/Import/AttributeException.php b/app/Exceptions/Import/AttributeException.php
new file mode 100644
index 0000000..eab14ce
--- /dev/null
+++ b/app/Exceptions/Import/AttributeException.php
@@ -0,0 +1,7 @@
+transform(function($item) {
+ return [
+ 'title'=>$item->getRdn(),
+ 'item'=>$item->getDNSecure(),
+ 'lazy'=>TRUE,
+ 'icon'=>'fa-fw fas fa-sitemap',
+ 'tooltip'=>$item->getDn(),
+ ];
+ });
+ }
+
/**
* Debug Page
*
@@ -49,6 +68,22 @@ class HomeController extends Controller
->with('page_actions',$page_actions);
}
+ public function entry_export(Request $request,string $id)
+ {
+ $dn = Crypt::decryptString($id);
+
+ $result = (new Entry)
+ ->query()
+ //->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time')))
+ //->select(['*'])
+ ->setDn($dn)
+ ->recursive()
+ ->get();
+
+ return view('fragment.export')
+ ->with('result',new LDIFExport($result));
+ }
+
public function entry_newattr(string $id)
{
$x = new AttributeType(new Attribute($id,[]),TRUE);
@@ -76,20 +111,8 @@ class HomeController extends Controller
->withInput()
->with('note',__('No attributes changed'));
- $base = Server::baseDNs() ?: collect();
-
- $bases = $base->transform(function($item) {
- return [
- 'title'=>$item->getRdn(),
- 'item'=>$item->getDNSecure(),
- 'lazy'=>TRUE,
- 'icon'=>'fa-fw fas fa-sitemap',
- 'tooltip'=>$item->getDn(),
- ];
- });
-
- return view('frames.update')
- ->with('bases',$bases)
+ return view('update')
+ ->with('bases',$this->bases())
->with('dn',$dn)
->with('o',$o);
}
@@ -103,18 +126,6 @@ class HomeController extends Controller
*/
public function entry_update(EntryRequest $request)
{
- $base = Server::baseDNs() ?: collect();
-
- $bases = $base->transform(function($item) {
- return [
- 'title'=>$item->getRdn(),
- 'item'=>$item->getDNSecure(),
- 'lazy'=>TRUE,
- 'icon'=>'fa-fw fas fa-sitemap',
- 'tooltip'=>$item->getDn(),
- ];
- });
-
$dn = Crypt::decryptString($request->dn);
$o = config('server')->fetch($dn);
@@ -168,51 +179,75 @@ class HomeController extends Controller
*/
public function home()
{
- $base = Server::baseDNs() ?: collect();
-
- $bases = $base->transform(function($item) {
- return [
- 'title'=>$item->getRdn(),
- 'item'=>$item->getDNSecure(),
- 'lazy'=>TRUE,
- 'icon'=>'fa-fw fas fa-sitemap',
- 'tooltip'=>$item->getDn(),
- ];
- });
-
if (old('dn'))
return view('frame')
->with('subframe','dn')
- ->with('bases',$bases)
+ ->with('bases',$this->bases())
->with('o',config('server')->fetch($dn=Crypt::decryptString(old('dn'))))
->with('dn',$dn);
elseif (old('frame'))
return view('frame')
->with('subframe',old('frame'))
- ->with('bases',$bases);
+ ->with('bases',$this->bases());
else
return view('home')
- ->with('bases',$bases)
+ ->with('bases',$this->bases())
->with('server',config('ldap.connections.default.name'));
}
+ /**
+ * Process the incoming LDIF file or LDIF text
+ *
+ * @param ImportRequest $request
+ * @param string $type
+ * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
+ * @throws GeneralException
+ * @throws VersionException
+ */
+ public function import(ImportRequest $request,string $type)
+ {
+ switch ($type) {
+ case 'ldif':
+ $import = new LDIFImport($x=($request->text ?: $request->file->get()));
+ break;
+
+ default:
+ abort(404,'Unknown import type: '.$type);
+ }
+
+ try {
+ $result = $import->process();
+
+ } catch (NotImplementedException $e) {
+ abort(555,$e->getMessage());
+
+ } catch (\Exception $e) {
+ abort(598,$e->getMessage());
+ }
+
+ return view('frame')
+ ->with('subframe','import_result')
+ ->with('bases',$this->bases())
+ ->with('result',$result)
+ ->with('ldif',htmlspecialchars($x));
+ }
+
+ public function import_frame()
+ {
+ return view('frames.import');
+ }
+
/**
* LDAP Server INFO
*
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
- * @throws ObjectNotFoundException
*/
public function info()
{
- // Load our attributes
- $s = config('server');
- $s->schema('objectclasses');
- $s->schema('attributetypes');
-
return view('frames.info')
- ->with('s',$s);
+ ->with('s',config('server'));
}
/**
diff --git a/app/Http/Requests/ImportRequest.php b/app/Http/Requests/ImportRequest.php
new file mode 100644
index 0000000..abdf8c1
--- /dev/null
+++ b/app/Http/Requests/ImportRequest.php
@@ -0,0 +1,22 @@
+ 'required|string|in:import',
+ 'file' => 'nullable|extensions:ldif|required_without:text',
+ 'text'=> 'nullable|prohibits:file|string|min:16',
+ ];
+ }
+}
\ No newline at end of file
diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php
index 9fc0726..edc1e18 100644
--- a/app/Ldap/Entry.php
+++ b/app/Ldap/Entry.php
@@ -2,21 +2,50 @@
namespace App\Ldap;
-use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Crypt;
+use LdapRecord\Support\Arr;
use LdapRecord\Models\Model;
+use LdapRecord\Query\Model\Builder;
use App\Classes\LDAP\Attribute;
use App\Classes\LDAP\Attribute\Factory;
+use App\Classes\LDAP\Export\LDIF;
+use App\Exceptions\Import\AttributeException;
class Entry extends Model
{
+ private Collection $objects;
+ private bool $noObjectAttributes = FALSE;
+
/* OVERRIDES */
+ public function __construct(array $attributes = [])
+ {
+ $this->objects = collect();
+
+ parent::__construct($attributes);
+ }
+
+ public function discardChanges(): static
+ {
+ parent::discardChanges();
+
+ // If we are discharging changes, we need to reset our $objects;
+ $this->objects = $this->getAttributesAsObjects($this->attributes);
+
+ return $this;
+ }
+
+ /**
+ * This function overrides getAttributes to use our collection of Attribute objects instead of the models attributes.
+ *
+ * @return array
+ * @note $this->attributes may not be updated with changes
+ */
public function getAttributes(): array
{
- return $this->getAttributesAsObjects()->toArray();
+ return $this->objects->map(function($item) { return $item->values->toArray(); })->toArray();
}
/**
@@ -24,57 +53,80 @@ class Entry extends Model
*/
protected function originalIsEquivalent(string $key): bool
{
- if (! array_key_exists($key, $this->original)) {
- return false;
+ $key = $this->normalizeAttributeKey($key);
+
+ if ((! array_key_exists($key, $this->original)) && (! $this->objects->has($key))) {
+ return TRUE;
}
$current = $this->attributes[$key];
- $original = $this->original[$key];
+ $original = $this->objects->get($key)->values;
if ($current === $original) {
return true;
}
- //dump(['key'=>$key,'current'=>$current,'original'=>$this->original[$key],'objectvalue'=>$this->getAttributeAsObject($key)->isDirty()]);
- return ! $this->getAttributeAsObject($key)->isDirty();
+ return ! $this->getObject($key)->isDirty();
}
- public function getOriginal(): array
+ public static function query(bool $noattrs=false): Builder
{
- static $result = NULL;
+ $o = new static;
- if (is_null($result)) {
- $result = collect();
+ if ($noattrs)
+ $o->noObjectAttributes();
- // @todo Optimise this foreach with getAttributes()
- foreach (parent::getOriginal() as $attribute => $value) {
- // If the attribute name has language tags
- $matches = [];
- if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
- $attribute = $matches[1];
+ return $o->newQuery();
+ }
- // If the attribute doesnt exist we'll create it
- $o = Arr::get($result,$attribute,Factory::create($attribute,[]));
- $o->setLangTag($matches[3],$value);
+ /**
+ * As attribute values are updated, or new ones created, we need to mirror that
+ * into our $objects
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return $this
+ */
+ public function setAttribute(string $key, mixed $value): static
+ {
+ parent::setAttribute($key,$value);
- } else {
- $o = Factory::create($attribute,$value);
- }
+ $key = $this->normalizeAttributeKey($key);
- if (! $result->has($attribute)) {
- // Set the rdn flag
- if (preg_match('/^'.$attribute.'=/i',$this->dn))
- $o->setRDN();
+ if ((! $this->objects->get($key)) && $value) {
+ $o = new Attribute($key,[]);
+ $o->value = $value;
- // Set required flag
- $o->required_by(collect($this->getAttribute('objectclass')));
+ $this->objects->put($key,$o);
- $result->put($attribute,$o);
- }
- }
+ } elseif ($this->objects->get($key)) {
+ $this->objects->get($key)->value = $this->attributes[$key];
}
- return $result->toArray();
+ return $this;
+ }
+
+ /**
+ * We'll shadow $this->attributes to $this->objects - a collection of Attribute objects
+ *
+ * Using the objects, it'll make it easier to work with attribute values
+ *
+ * @param array $attributes
+ * @return $this
+ */
+ public function setRawAttributes(array $attributes = []): static
+ {
+ parent::setRawAttributes($attributes);
+
+ // We only set our objects on DN entries (otherwise we might get into a recursion loop if this is the schema DN)
+ if ($this->dn && (! in_array($this->dn,Arr::get($this->attributes,'subschemasubentry',[])))) {
+ $this->objects = $this->getAttributesAsObjects($this->attributes);
+
+ } else {
+ $this->objects = collect();
+ }
+
+ return $this;
}
/* ATTRIBUTES */
@@ -92,88 +144,89 @@ class Entry extends Model
/* METHODS */
- /**
- * Get an attribute as an object
- *
- * @param string $key
- * @return Attribute|null
- */
- public function getAttributeAsObject(string $key): Attribute|null
+ public function addAttribute(string $key,mixed $value): void
{
- return Arr::get($this->getAttributesAsObjects(),$key);
+ $key = $this->normalizeAttributeKey($key);
+
+ if (config('server')->schema('attributetypes')->has($key) === FALSE)
+ throw new AttributeException('Schema doesnt have attribute [%s]',$key);
+
+ if ($x=$this->objects->get($key)) {
+ $x->addValue($value);
+
+ } else {
+ $this->objects->put($key,Attribute\Factory::create($key,Arr::wrap($value)));
+ }
}
/**
* Convert all our attribute values into an array of Objects
*
+ * @param array $attributes
* @return Collection
*/
- protected function getAttributesAsObjects(): Collection
+ protected function getAttributesAsObjects(array $attributes): Collection
{
- static $result = NULL;
+ $result = collect();
- if (is_null($result)) {
- $result = collect();
+ foreach ($attributes as $attribute => $value) {
+ // If the attribute name has language tags
+ $matches = [];
+ if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
+ $attribute = $matches[1];
- foreach (parent::getAttributes() as $attribute => $value) {
- // If the attribute name has language tags
- $matches = [];
- if (preg_match('/^([a-zA-Z]+)(;([a-zA-Z-;]+))+/',$attribute,$matches)) {
- $attribute = $matches[1];
+ // If the attribute doesnt exist we'll create it
+ $o = Arr::get($result,$attribute,Factory::create($attribute,[]));
+ $o->setLangTag($matches[3],$value);
- // If the attribute doesnt exist we'll create it
- $o = Arr::get($result,$attribute,Factory::create($attribute,[]));
- $o->setLangTag($matches[3],$value);
-
- } else {
- $o = Factory::create($attribute,$value);
- }
-
- 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')));
-
- // Store our original value to know if this attribute has changed
- if ($x=Arr::get($this->original,$attribute))
- $o->oldValues($x);
-
- $result->put($attribute,$o);
- }
+ } else {
+ $o = Factory::create($attribute,$value);
}
- $sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
+ if (! $result->has($attribute)) {
+ // Set the rdn flag
+ if (preg_match('/^'.$attribute.'=/i',$this->dn))
+ $o->setRDN();
- // Order the attributes
- $result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
- if ($a === $b)
- return 0;
+ // Set required flag
+ $o->required_by(collect($this->getAttribute('objectclass')));
- // Check if $a/$b are in the configuration to be sorted first, if so get it's key
- $a_key = $sort->search($a->name_lc);
- $b_key = $sort->search($b->name_lc);
+ // Store our original value to know if this attribute has changed
+ if ($x=Arr::get($this->original,$attribute))
+ $o->oldValues($x);
- // If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
- if ($a_key === FALSE)
- $a_key = $sort->count()+1;
-
- if ($b_key === FALSE)
- $b_key = $sort->count()+1;
-
- // Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
- // So we sort them alphabetically
- if ($a_key === $b_key)
- return strcasecmp($a->name,$b->name);
-
- // Case where at least one attribute or its friendly name is in $attrs_display_order
- // return -1 if $a before $b in $attrs_display_order
- return ($a_key < $b_key) ? -1 : 1;
- } ]);
+ $result->put($attribute,$o);
+ }
}
+ $sort = collect(config('ldap.attr_display_order',[]))->transform(function($item) { return strtolower($item); });
+
+ // Order the attributes
+ $result = $result->sortBy([function(Attribute $a,Attribute $b) use ($sort): int {
+ if ($a === $b)
+ return 0;
+
+ // Check if $a/$b are in the configuration to be sorted first, if so get it's key
+ $a_key = $sort->search($a->name_lc);
+ $b_key = $sort->search($b->name_lc);
+
+ // If the keys were not in the sort list, set the key to be the count of elements (ie: so it is last to be sorted)
+ if ($a_key === FALSE)
+ $a_key = $sort->count()+1;
+
+ if ($b_key === FALSE)
+ $b_key = $sort->count()+1;
+
+ // Case where neither $a, nor $b are in ldap.attr_display_order, $a_key = $b_key = one greater than num elements.
+ // So we sort them alphabetically
+ if ($a_key === $b_key)
+ return strcasecmp($a->name,$b->name);
+
+ // Case where at least one attribute or its friendly name is in $attrs_display_order
+ // return -1 if $a before $b in $attrs_display_order
+ return ($a_key < $b_key) ? -1 : 1;
+ } ]);
+
return $result;
}
@@ -208,11 +261,31 @@ class Entry extends Model
*/
public function getInternalAttributes(): Collection
{
- return collect($this->getAttributes())->filter(function($item) {
+ return $this->objects->filter(function($item) {
return $item->is_internal;
});
}
+ /**
+ * Get an attribute as an object
+ *
+ * @param string $key
+ * @return Attribute|null
+ */
+ public function getObject(string $key): Attribute|null
+ {
+ return $this->objects->get($this->normalizeAttributeKey($key));
+ }
+
+ public function getObjects(): Collection
+ {
+ // In case we havent built our objects yet (because they werent available while determining the schema DN)
+ if ((! $this->objects->count()) && $this->attributes)
+ $this->objects = $this->getAttributesAsObjects($this->attributes);
+
+ return $this->objects;
+ }
+
/**
* Return a list of attributes without any values
*
@@ -230,11 +303,46 @@ class Entry extends Model
*/
public function getVisibleAttributes(): Collection
{
- return collect($this->getAttributes())->filter(function($item) {
+ return $this->objects->filter(function($item) {
return ! $item->is_internal;
});
}
+ public function hasAttribute(int|string $key): bool
+ {
+ return $this->objects->has($key);
+ }
+
+ /**
+ * Export this record
+ *
+ * @param string $method
+ * @param string $scope
+ * @return string
+ * @throws \Exception
+ */
+ public function export(string $method,string $scope): string
+ {
+ // @todo To implement
+ switch ($scope) {
+ case 'base':
+ case 'one':
+ case 'sub':
+ break;
+
+ default:
+ throw new \Exception('Export scope unknown:'.$scope);
+ }
+
+ switch ($method) {
+ case 'ldif':
+ return new LDIF(collect($this));
+
+ default:
+ throw new \Exception('Export method not implemented:'.$method);
+ }
+ }
+
/**
* Return an icon for a DN based on objectClass
*
@@ -300,4 +408,16 @@ class Entry extends Model
// Default
return 'fa-fw fas fa-cog';
}
+
+ /**
+ * Dont convert our $this->attributes to $this->objects when creating a new Entry::class
+ *
+ * @return $this
+ */
+ public function noObjectAttributes(): static
+ {
+ $this->noObjectAttributes = TRUE;
+
+ return $this;
+ }
}
\ No newline at end of file
diff --git a/htdocs/export.php b/htdocs/export.php
deleted file mode 100755
index cc1096b..0000000
--- a/htdocs/export.php
+++ /dev/null
@@ -1,40 +0,0 @@
-getIndex(),get_request('exporter_id','REQUEST'));
-$request['export'] = $request['exporter']->getTemplate();
-$types = $request['export']->getType();
-
-# send the header
-if ($request['file']) {
- $obStatus = ob_get_status();
- if (isset($obStatus['type']) && $obStatus['type'] && $obStatus['status'])
- ob_end_clean();
-
- header('Content-type: application/download');
- header(sprintf('Content-Disposition: inline; filename="%s.%s"','export',$types['extension'].($request['export']->isCompressed() ? '.gz' : '')));
- echo $request['export']->export();
- die();
-
-} else {
- print '';
- echo htmlspecialchars($request['export']->export());
- print '
';
-}
-?>
diff --git a/htdocs/import_form.php b/htdocs/import_form.php
deleted file mode 100644
index 9c6bbbb..0000000
--- a/htdocs/import_form.php
+++ /dev/null
@@ -1,48 +0,0 @@
-getIndex(),get_request('template','REQUEST',false,'none'));
-$request['page']->drawTitle(sprintf('%s',_('Import')));
-$request['page']->drawSubTitle(sprintf('%s: %s',_('Server'),$app['server']->getName()));
-
-echo '