diff --git a/app/Classes/Template.php b/app/Classes/Template.php new file mode 100644 index 00000000..8cd6fb30 --- /dev/null +++ b/app/Classes/Template.php @@ -0,0 +1,46 @@ +file = $file; + + try { + $this->template = json_decode($td->get($file),null,512,JSON_OBJECT_AS_ARRAY|JSON_THROW_ON_ERROR); + + } catch (\JsonException $e) { + $this->invalid = TRUE; + $this->reason = $e->getMessage(); + } + } + + public function __get(string $key): mixed + { + return match ($key) { + 'attributes' => array_map('strtolower',array_keys(Arr::get($this->template,$key))), + 'objectclasses' => array_map('strtolower',Arr::get($this->template,$key)), + 'enabled' => Arr::get($this->template,$key,FALSE), + 'icon','regexp' => Arr::get($this->template,$key), + + default => throw new \Exception('Unknown key: '.$key), + }; + } + + public function __toString(): string + { + return $this->invalid ? '' : Arr::get($this->template,'title','No Template Name'); + } +} \ No newline at end of file diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index 63243e44..faa89187 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -4,9 +4,12 @@ namespace App\Ldap; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Crypt; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; use LdapRecord\Support\Arr; use LdapRecord\Models\Model; +use App\Classes\Template; use App\Classes\LDAP\Attribute; use App\Classes\LDAP\Attribute\Factory; use App\Classes\LDAP\Export\LDIF; @@ -39,9 +42,19 @@ class Entry extends Model public function __construct(array $attributes = []) { $this->objects = collect(); - $this->templates = collect(['default'=>__('LDAP Entry')]); parent::__construct($attributes); + + // Load any templates + $x = Storage::disk(config('pla.template.dir')); + $this->templates = collect(); + + foreach (array_filter($x->files(),fn($item)=>Str::endsWith($item,'.json')) as $file) + $this->templates->put($file,new Template($file)); + + $this->templates = $this->templates + ->filter(fn($item)=>(! $item->invalid) && $item->enabled) + ->sortBy(fn($item)=>$item); } public function discardChanges(): static @@ -131,6 +144,13 @@ class Entry extends Model $this->objects = collect(); } + // Filter out our templates specific for this entry + if ($this->dn && (! in_array(strtolower($this->dn),['cn=subschema']))) { + $this->templates = $this->templates + ->filter(fn($item)=>(! $item->regexp) || preg_match($item->regexp,$this->dn)) + ->filter(fn($item)=>! count(array_diff($item->objectclasses,array_map('strtolower',Arr::get($this->attributes,'objectclass'))))); + } + return $this; } @@ -531,4 +551,9 @@ class Entry extends Model $this->rdnbase = $bdn; } + + public function template(string $item): Template|Null + { + return Arr::get($this->templates,$item); + } } \ No newline at end of file diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 00000000..6c9a2400 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,10 @@ + [ + 'templates' => [ + 'driver' => 'local', + 'root' => base_path(env('LDAP_TEMPLATE_DIR','templates')), + ], + ], +]; diff --git a/config/pla.php b/config/pla.php index e3d0f752..bbb09f75 100644 --- a/config/pla.php +++ b/config/pla.php @@ -76,4 +76,8 @@ return [ 'attr' => [env('LDAP_LOGIN_ATTR','uid') => env('LDAP_LOGIN_ATTR_DESC','User ID')], // Attribute used to find user for login 'objectclass' => explode(',',env('LDAP_LOGIN_OBJECTCLASS', 'posixAccount')), // Objectclass that users must contain to login ], + + 'template' => [ + 'dir' => env('LDAP_TEMPLATE_DRIVER','templates'), + ], ]; \ No newline at end of file diff --git a/resources/views/fragment/template/dn.blade.php b/resources/views/fragment/template/dn.blade.php new file mode 100644 index 00000000..326bd0bf --- /dev/null +++ b/resources/views/fragment/template/dn.blade.php @@ -0,0 +1,17 @@ + +
+ @csrf + + + +
+
+ @php($up=(session()->pull('updated') ?: collect())) + @php($attributes=$o->template($template)?->attributes) + + @foreach($o->getVisibleAttributes()->filter(fn($item)=>in_array($item,$attributes)) as $ao) + + @endforeach +
+
+
\ No newline at end of file diff --git a/resources/views/frames/dn.blade.php b/resources/views/frames/dn.blade.php index 480ebcac..b581795a 100644 --- a/resources/views/frames/dn.blade.php +++ b/resources/views/frames/dn.blade.php @@ -74,53 +74,49 @@
@foreach($o->templates as $template => $name) - @switch($template) - @case('default') -
$loop->index === 0]) id="template-{{$template}}" role="tabpanel"> -
- @csrf - - - -
-
- @php($up=(session()->pull('updated') ?: collect())) - @foreach($o->getVisibleAttributes() as $ao) - - @endforeach - - @include('fragment.dn.add_attr') -
-
-
-
- @break - - @default -
$loop->index === 0]) id="template-{{$template}}" role="tabpanel"> -

{{$name}}

-
- @endswitch +
$loop->index === 0]) id="template-{{$template}}" role="tabpanel"> + @include('fragment.template.dn',['template'=>$template]) +
@endforeach -
-
- -
-
- - +
(! $o->templates->count())]) id="template-default" role="tabpanel"> +
+ @csrf + + + +
+
+ @php($up=(session()->pull('updated') ?: collect())) + @foreach($o->getVisibleAttributes() as $ao) + + @endforeach + + @include('fragment.dn.add_attr') +
+
+
+ +
+
+ + +
+
+
+
@@ -162,7 +158,6 @@ // Find all input items and turn off readonly $('input.form-control').each(function() { - // Except for objectClass - @todo show an "X" instead if ($(this)[0].name.match(/^objectclass/)) return; diff --git a/templates/dns_domain.json b/templates/dns_domain.json new file mode 100644 index 00000000..afb02f69 --- /dev/null +++ b/templates/dns_domain.json @@ -0,0 +1,22 @@ +{ + "title": "Generic: DNS Entry", + "description": "New DNS Entry", + "enabled": true, + "icon": "fa-globe", + "rdn": "dc", + + "objectclasses": [ + "dnsDomain" + ], + + "attributes": { + "dc": { + "display": "Domain Component", + "order": 1 + }, + "associatedDomain": { + "display": "Associated Domain", + "order": 2 + } + } +} diff --git a/templates/example.json b/templates/example.json new file mode 100644 index 00000000..b775b879 --- /dev/null +++ b/templates/example.json @@ -0,0 +1,25 @@ +{ + "title": "Example entry", + "description": "This is the description", + "enabled": false, + "icon": "fa-star-of-life", + "rdn": "o", + "regexp": "/^$/", + + "objectclasses": [ + "organization" + ], + + "attributes": { + "attribute1": { + "display": "Attribute 1", + "hint": "This is an example", + "order": 1 + }, + "attribute2": { + "display": "Attribute 2", + "hint": "This is an example", + "order": 2 + } + } +} diff --git a/templates/o.json b/templates/o.json new file mode 100644 index 00000000..ba203fa3 --- /dev/null +++ b/templates/o.json @@ -0,0 +1,20 @@ +{ + "title": "Generic: Organisational", + "description": "New Organisational", + "enabled": true, + "icon": "fa-building", + "rdn": "ou", + "regexp": "/^o=/", + + "objectclasses": [ + "organization" + ], + + "attributes": { + "o": { + "display": "Organisation", + "hint": "This is an example", + "order": 1 + } + } +} diff --git a/templates/ou.json b/templates/ou.json new file mode 100644 index 00000000..8050f52b --- /dev/null +++ b/templates/ou.json @@ -0,0 +1,25 @@ +{ + "title": "Generic: Organisational Unit", + "description": "New Organisational Unit", + "enabled": true, + "icon": "fa-layer-group", + "rdn": "ou", + "regexp": "/^ou=.+,o=/", + + "objectclasses": [ + "organizationalUnit" + ], + + "attributes": { + "ou": { + "display": "Organisational Unit", + "hint": "This is an example", + "order": 1 + }, + "attribute2": { + "display": "Attribute 2", + "hint": "This is an example", + "order": 2 + } + } +} diff --git a/templates/user_account.json b/templates/user_account.json new file mode 100644 index 00000000..bc6c5efa --- /dev/null +++ b/templates/user_account.json @@ -0,0 +1,82 @@ +{ + "title": "Generic: User Account", + "description": "New User Account", + "enabled": true, + "icon": "fa-user", + "rdn": "cn", + "regexp": "/,ou=People,o=/", + + "objectclasses": [ + "inetOrgPerson", + "posixAccount" + ], + + "attributes": { + "givenName": { + "display": "First Name", + "onchange": [ + "=autoFill(cn;%givenName% %sn%)", + "=autoFill(uid;%givenName|0-1/l%%sn/l%)" + ], + "order": 1 + }, + "sn": { + "display": "Last Name", + "onchange": [ + "=autoFill(cn;%givenName% %sn%)", + "=autoFill(uid;%givenName|0-1/l%%sn/l%)" + ], + "order": 2 + }, + "cn": { + "display": "Common Name", + "readonly": true, + "order": 3 + }, + "uid": { + "display": "User ID", + "onchange": [ + "=autoFill(homeDirectory;/home/users/%uid%)" + ], + "order": 4 + }, + "userPassword": { + "display": "Password", + "order": 5 + }, + "uidNumber": { + "display": "UID Number", + "readonly": true, + "value": "=php.GetNextNumber(/;uidNumber)", + "order": 6 + }, + "gidNumber": { + "display": "UID Number", + "readonly": true, + "onchange": [ + "=autoFill(homeDirectory;/home/users/%gidNumber|0-0/T%/%uid|3-%)" + ], + "value": "=php.GetNextNumber(/;uidNumber)", + "value": "=php.PickList(/;(&(objectClass=posixGroup));gidNumber;%cn%;;;;cn)", + "order": 7 + }, + "homeDirectory": { + "display": "Home Directory", + "order": 8 + }, + "loginShell": { + "display": "Login Shell", + "select": { + "/bin/bash": "Bash", + "/bin/csh": "C Shell", + "/bin/dash": "Dash", + "/bin/sh": "Shell", + "/bin/tsh": "Turbo C Shell", + "/bin/zsh": "ZSH", + "/bin/false": "False", + "/usr/sbin/nologin": "No Login" + }, + "order": 9 + } + } +}