From 652cdee034a132b4dd220b5c1fb74d00a244696a Mon Sep 17 00:00:00 2001 From: Deon George Date: Sat, 2 Sep 2023 20:50:54 +1000 Subject: [PATCH] Enabled adding new attributes to a DN --- app/Classes/LDAP/Attribute.php | 4 +- .../LDAP/Attribute/Binary/JpegPhoto.php | 3 +- app/Classes/LDAP/Attribute/Internal.php | 2 +- .../LDAP/Attribute/Internal/Timestamp.php | 2 +- app/Classes/LDAP/Attribute/ObjectClass.php | 2 +- app/Classes/LDAP/Attribute/Password.php | 3 +- app/Classes/LDAP/Attribute/Schema.php | 2 +- .../LDAP/Attribute/Schema/Mechanisms.php | 2 +- app/Classes/LDAP/Attribute/Schema/OID.php | 2 +- app/Http/Controllers/HomeController.php | 82 +++++++++++-- app/View/Components/Attribute.php | 6 +- app/View/Components/AttributeType.php | 34 ++++++ config/ldap.php | 2 +- public/js/custom.js | 2 +- .../views/components/attribute-type.blade.php | 20 ++++ .../views/components/attribute.blade.php | 25 ++-- .../attribute/binary/jpegphoto.blade.php | 50 ++++---- .../attribute/objectclass.blade.php | 13 +- .../attribute/widget/options.blade.php | 34 ++++++ resources/views/frames/dn.blade.php | 103 ++++++++++------ resources/views/frames/update.blade.php | 111 ++++++++++++++++++ routes/web.php | 4 +- 22 files changed, 403 insertions(+), 105 deletions(-) create mode 100644 app/View/Components/AttributeType.php create mode 100644 resources/views/components/attribute-type.blade.php create mode 100644 resources/views/components/attribute/widget/options.blade.php create mode 100644 resources/views/frames/update.blade.php diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php index f3f6b73..afa04bf 100644 --- a/app/Classes/LDAP/Attribute.php +++ b/app/Classes/LDAP/Attribute.php @@ -97,6 +97,7 @@ class Attribute $this->name = $name; $this->values = collect($values); $this->lang_tags = collect(); + $this->required_by = collect(); // No need to load our schema for internal attributes if (! $this->is_internal) @@ -182,9 +183,10 @@ class Attribute * Display the attribute value * * @param bool $edit + * @param bool $blank * @return View */ - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { return view('components.attribute') ->with('edit',$edit) diff --git a/app/Classes/LDAP/Attribute/Binary/JpegPhoto.php b/app/Classes/LDAP/Attribute/Binary/JpegPhoto.php index e86dabc..13910f2 100644 --- a/app/Classes/LDAP/Attribute/Binary/JpegPhoto.php +++ b/app/Classes/LDAP/Attribute/Binary/JpegPhoto.php @@ -18,10 +18,11 @@ final class JpegPhoto extends Binary $this->internal = FALSE; } - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { return view('components.attribute.binary.jpegphoto') ->with('edit',$edit) + ->with('blank',$blank) ->with('o',$this) ->with('f',new \finfo); } diff --git a/app/Classes/LDAP/Attribute/Internal.php b/app/Classes/LDAP/Attribute/Internal.php index 38ab392..d0c6029 100644 --- a/app/Classes/LDAP/Attribute/Internal.php +++ b/app/Classes/LDAP/Attribute/Internal.php @@ -13,7 +13,7 @@ abstract class Internal extends Attribute { protected bool $is_internal = TRUE; - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { // @note Internal attributes cannot be edited return view('components.attribute.internal') diff --git a/app/Classes/LDAP/Attribute/Internal/Timestamp.php b/app/Classes/LDAP/Attribute/Internal/Timestamp.php index 587e423..64adf22 100644 --- a/app/Classes/LDAP/Attribute/Internal/Timestamp.php +++ b/app/Classes/LDAP/Attribute/Internal/Timestamp.php @@ -11,7 +11,7 @@ use App\Classes\LDAP\Attribute\Internal; */ final class Timestamp extends Internal { - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { // @note Internal attributes cannot be edited return view('components.attribute.internal.timestamp') diff --git a/app/Classes/LDAP/Attribute/ObjectClass.php b/app/Classes/LDAP/Attribute/ObjectClass.php index f297d1a..fa6b0fe 100644 --- a/app/Classes/LDAP/Attribute/ObjectClass.php +++ b/app/Classes/LDAP/Attribute/ObjectClass.php @@ -39,7 +39,7 @@ final class ObjectClass extends Attribute return $this->structural->search($value) !== FALSE; } - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { return view('components.attribute.objectclass') ->with('edit',$edit) diff --git a/app/Classes/LDAP/Attribute/Password.php b/app/Classes/LDAP/Attribute/Password.php index 18232e5..d1842b1 100644 --- a/app/Classes/LDAP/Attribute/Password.php +++ b/app/Classes/LDAP/Attribute/Password.php @@ -11,10 +11,11 @@ use App\Classes\LDAP\Attribute; */ final class Password extends Attribute { - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { return view('components.attribute.password') ->with('edit',$edit) + ->with('blank',$blank) ->with('o',$this); } } \ No newline at end of file diff --git a/app/Classes/LDAP/Attribute/Schema.php b/app/Classes/LDAP/Attribute/Schema.php index 0acf31e..be47dfb 100644 --- a/app/Classes/LDAP/Attribute/Schema.php +++ b/app/Classes/LDAP/Attribute/Schema.php @@ -49,7 +49,7 @@ abstract class Schema extends Attribute return Arr::get(($array ? $array->get($string) : []),$key); } - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { // @note Schema attributes cannot be edited return view('components.attribute.internal') diff --git a/app/Classes/LDAP/Attribute/Schema/Mechanisms.php b/app/Classes/LDAP/Attribute/Schema/Mechanisms.php index a76da71..6cf4480 100644 --- a/app/Classes/LDAP/Attribute/Schema/Mechanisms.php +++ b/app/Classes/LDAP/Attribute/Schema/Mechanisms.php @@ -33,7 +33,7 @@ final class Mechanisms extends Schema return parent::_get(config_path('ldap_supported_saslmechanisms.txt'),$string,$key); } - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { // @note Schema attributes cannot be edited return view('components.attribute.schema.mechanisms') diff --git a/app/Classes/LDAP/Attribute/Schema/OID.php b/app/Classes/LDAP/Attribute/Schema/OID.php index 4c03011..1c0a26e 100644 --- a/app/Classes/LDAP/Attribute/Schema/OID.php +++ b/app/Classes/LDAP/Attribute/Schema/OID.php @@ -34,7 +34,7 @@ final class OID extends Schema return parent::_get(config_path('ldap_supported_oids.txt'),$string,$key); } - public function render(bool $edit=FALSE): View + public function render(bool $edit=FALSE,bool $blank=FALSE): View { // @note Schema attributes cannot be edited return view('components.attribute.schema.oid') diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index e24d431..0840c7c 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -8,14 +8,16 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\File; -use Illuminate\Support\Facades\Session; +use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Redirect; use LdapRecord\Exceptions\InsufficientAccessException; use LdapRecord\LdapRecordException; use LdapRecord\Query\ObjectNotFoundException; -use App\Classes\LDAP\Server; +use App\Classes\LDAP\{Attribute,Server}; use App\Exceptions\InvalidUsage; use App\Http\Requests\EntryRequest; +use App\View\Components\AttributeType; class HomeController extends Controller { @@ -47,7 +49,20 @@ class HomeController extends Controller ->with('page_actions',$page_actions); } - public function entry_update(EntryRequest $request) + public function entry_newattr(string $id) + { + $x = new AttributeType(new Attribute($id,[]),TRUE); + return $x->render(); + } + + /** + * Show a confirmation to update a DN + * + * @param EntryRequest $request + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse + * @throws ObjectNotFoundException + */ + public function entry_pending_update(EntryRequest $request) { $dn = Crypt::decryptString($request->dn); @@ -56,10 +71,60 @@ class HomeController extends Controller foreach ($request->except(['_token','dn']) as $key => $value) $o->{$key} = array_filter($value); - Session::put('dn',$request->dn); + if (! $o->getDirty()) + return back() + ->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) + ->with('dn',$dn) + ->with('o',$o); + } + + /** + * Update a DN entry + * + * @param EntryRequest $request + * @return \Illuminate\Http\RedirectResponse + * @throws ObjectNotFoundException + */ + 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); + + foreach ($request->except(['_token','dn']) as $key => $value) + $o->{$key} = array_filter($value); if (! $dirty=$o->getDirty()) return back() + ->withInput() ->with('note',__('No attributes changed')); try { @@ -70,7 +135,8 @@ class HomeController extends Controller switch ($x=$e->getDetailedError()->getErrorCode()) { case 50: - return back() + return Redirect::to('/') + ->withInput() ->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage()))); default: @@ -82,7 +148,8 @@ class HomeController extends Controller switch ($x=$e->getDetailedError()->getErrorCode()) { case 8: - return back() + return Redirect::to('/') + ->withInput() ->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage()))); default: @@ -90,7 +157,8 @@ class HomeController extends Controller } } - return back() + return Redirect::to('/') + ->withInput() ->with('success',__('Entry updated')) ->with('updated',$dirty); } diff --git a/app/View/Components/Attribute.php b/app/View/Components/Attribute.php index ff40a6b..58168e6 100644 --- a/app/View/Components/Attribute.php +++ b/app/View/Components/Attribute.php @@ -10,16 +10,18 @@ class Attribute extends Component { public LDAPAttribute $o; public bool $edit; + public bool $new; /** * Create a new component instance. * * @return void */ - public function __construct(bool $edit,LDAPAttribute $o) + public function __construct(bool $edit,LDAPAttribute $o,bool $new=FALSE) { $this->edit = $edit; $this->o = $o; + $this->new = $new; } /** @@ -29,6 +31,6 @@ class Attribute extends Component */ public function render() { - return $this->o->render($this->edit); + return $this->o->render($this->edit,$this->new); } } \ No newline at end of file diff --git a/app/View/Components/AttributeType.php b/app/View/Components/AttributeType.php new file mode 100644 index 0000000..5608e12 --- /dev/null +++ b/app/View/Components/AttributeType.php @@ -0,0 +1,34 @@ +o = $o; + $this->new = $new; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('components.attribute-type') + ->with('o',$this->o) + ->with('new',$this->new); + } +} \ No newline at end of file diff --git a/config/ldap.php b/config/ldap.php index 4596e61..feaf538 100644 --- a/config/ldap.php +++ b/config/ldap.php @@ -123,7 +123,7 @@ return [ 'validation' => [ 'objectclass' => ['objectclass'=>['array','min:1']], 'gidnumber' => ['gidnumber'=>['sometimes','array','max:1'],'gidnumber.*'=>['integer','max:65535']], - 'mail' => ['mail'=>['sometimes','array','min:1'],'mail.*'=>['email']], + 'mail' => ['mail'=>['sometimes','array','min:1'],'mail.*'=>['nullable','email']], 'uidnumber' => ['uidnumber'=>['sometimes','array','max:1'],'uidnumber.*'=>['integer','max:65535']], ], ]; diff --git a/public/js/custom.js b/public/js/custom.js index e3ecf03..cab584a 100644 --- a/public/js/custom.js +++ b/public/js/custom.js @@ -13,7 +13,7 @@ function expandChildren(node) { function getNode(item) { $.ajax({ - url: 'dn', + url: '/dn', method: 'POST', data: { key: item }, dataType: 'html', diff --git a/resources/views/components/attribute-type.blade.php b/resources/views/components/attribute-type.blade.php new file mode 100644 index 0000000..e376b27 --- /dev/null +++ b/resources/views/components/attribute-type.blade.php @@ -0,0 +1,20 @@ + +
+
+
+
+
+ {{ $o->name }} + + + @foreach($o->hints as $name => $description) + @if ($loop->index),@endif + {{ $name }} + @endforeach + +
+
+ + +
+
\ No newline at end of file diff --git a/resources/views/components/attribute.blade.php b/resources/views/components/attribute.blade.php index 65f4163..51bcc59 100644 --- a/resources/views/components/attribute.blade.php +++ b/resources/views/components/attribute.blade.php @@ -1,31 +1,26 @@ -
-
+
+
+
- @foreach (old($o->name_lc,$o->values) as $value) + @foreach (old($o->name_lc,$new ? [NULL] : $o->values) as $value) @if ($edit && ! $o->is_rdn)
- + +
@if($e) {{ join('|',$e) }} @endif
+ @else - {{ $value }}
+ {{ $value }} @endif @endforeach
-
-
- @if($o->is_rdn) - @lang('Rename') - @elseif($edit && $o->can_addvalues) -
- @lang('Add Value') -
- @endif + @include('components.attribute.widget.options')
-
\ No newline at end of file +
diff --git a/resources/views/components/attribute/binary/jpegphoto.blade.php b/resources/views/components/attribute/binary/jpegphoto.blade.php index 06d35e8..13e8421 100644 --- a/resources/views/components/attribute/binary/jpegphoto.blade.php +++ b/resources/views/components/attribute/binary/jpegphoto.blade.php @@ -3,31 +3,31 @@
@endif - - - @foreach ($o->values as $value) - @switch ($x=$f->buffer($value,FILEINFO_MIME_TYPE)) - @case('image/jpeg') - @default - + @endswitch + @endforeach + +
- - + + + @foreach ($o->values as $value) + @switch ($x=$f->buffer($value,FILEINFO_MIME_TYPE)) + @case('image/jpeg') + @default + - @endswitch - @endforeach - -
+ + - @if($edit) -
@lang('Delete') - @endif -
- - @if($edit) -
- @if($e) - {{ join('|',$e) }} - @endif -
+ @if($edit) +
@lang('Delete') + @endif +
+@if($edit) +
+ @if($e) + {{ join('|',$e) }} + @endif
- @endif \ No newline at end of file + +
+@endif \ No newline at end of file diff --git a/resources/views/components/attribute/objectclass.blade.php b/resources/views/components/attribute/objectclass.blade.php index c831b6c..b7e88db 100644 --- a/resources/views/components/attribute/objectclass.blade.php +++ b/resources/views/components/attribute/objectclass.blade.php @@ -1,6 +1,7 @@
-
+
+
@foreach (old($o->name_lc,$o->values) as $value) @if ($edit && ($value === NULL || (! $o->isStructural($value)))) @@ -22,15 +23,7 @@ @endif @endforeach
-
-
- @if($o->is_rdn) - @lang('Rename') - @elseif($edit && $o->can_addvalues) -
- @lang('Add Value') -
- @endif + @include('components.attribute.widget.options')
\ No newline at end of file diff --git a/resources/views/components/attribute/widget/options.blade.php b/resources/views/components/attribute/widget/options.blade.php new file mode 100644 index 0000000..a27e8ff --- /dev/null +++ b/resources/views/components/attribute/widget/options.blade.php @@ -0,0 +1,34 @@ +@if($o->is_rdn) + @lang('Rename') +@elseif($edit && $o->can_addvalues) +
+ @lang('Add Value') + @if($new) + + @endif +
+@endif + +@section('page-scripts') + @if(($edit && $o->can_addvalues)) + + @endif +@append \ No newline at end of file diff --git a/resources/views/frames/dn.blade.php b/resources/views/frames/dn.blade.php index b17184b..a9d1486 100644 --- a/resources/views/frames/dn.blade.php +++ b/resources/views/frames/dn.blade.php @@ -49,6 +49,7 @@
@endif + @if($errors->any())

Error?

@@ -75,38 +76,48 @@
-
+ @csrf + @foreach ($o->getVisibleAttributes() as $ao) + + @endforeach + +
+ +
-
- - @foreach ($o->getVisibleAttributes() as $ao) - - - - - - - @endforeach -
- {{ $ao->name }} - - - @foreach($ao->hints as $name => $description) - @if ($loop->index),@endif - {{ $name }} - @endforeach - -
- -
+
+
+
+ + @if($o->getMissingAttributes()->count()) +
+
+ Add New Attribute +
+
+ +
+
+ + +
+
+ @endif +
+
-
+
@lang('Reset') @lang('Update') @@ -140,12 +151,15 @@
-
+
@dump($o)
-
+
@dump($o->getAttributes())
+
+ @dump(['available'=>$o->getAvailableAttributes()->pluck('name'),'missing'=>$o->getMissingAttributes()->pluck('name')]) +
@@ -167,22 +181,43 @@ $('.row.d-none').removeClass('d-none'); $('.addable.d-none').removeClass('d-none'); $('.deletable.d-none').removeClass('d-none'); + + @if($o->getMissingAttributes()->count()) + $('#newattr-select.d-none').removeClass('d-none'); + @endif } $(document).ready(function() { - $('#reset').click(function() { - $('#form-entry')[0].reset(); + $('#form-reset').click(function() { + $('#dn-edit')[0].reset(); }) $('#form-submit').click(function() { - $('#form-entry')[0].submit(); + $('#dn-edit')[0].submit(); }) - // Create a new entry when Add Value clicked - $('.addable').click(function(item) { - var cln = $(this).parent().parent().find('input:last').clone(); - cln.val('').attr('placeholder','[@lang('NEW')]'); - cln.appendTo('#'+item.currentTarget.id) + $('#newattr').on('change',function(item) { + $.ajax({ + type: 'GET', + beforeSend: function() { + }, + success: function(data) { + $('#newattrs').append(data); + }, + error: function(e) { + if (e.status != 412) + alert('That didnt work? Please try again....'); + }, + url: '{{ url('entry/newattr') }}/'+item.target.value, + cache: false + }) + + // Remove the option from the list + $(this).find('[value="'+item.target.value+'"]').remove() + + // If there are no more options + if ($(this).find("option").length === 1) + $('#newattr-select').remove(); }); $('button[id=entry-edit]').on('click',function(item) { diff --git a/resources/views/frames/update.blade.php b/resources/views/frames/update.blade.php new file mode 100644 index 0000000..951aab9 --- /dev/null +++ b/resources/views/frames/update.blade.php @@ -0,0 +1,111 @@ +@extends('dn') + +@section('page_title') + + + + + + + + +
{!! $x ? $x->render() : sprintf('
',$o->icon() ?? "fas fa-info") !!}
{{ $dn }}
+ + + + + + + + + + + + + +
Created{{ ($x=Arr::get($o->getAttributes(),'createtimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'creatorsname')) ? $x->render() : __('Unknown') }}]
Modified{{ ($x=Arr::get($o->getAttributes(),'modifytimestamp')) ? $x->render() : __('Unknown') }} [{{ ($x=Arr::get($o->getAttributes(),'modifiersname')) ? $x->render() : __('Unknown') }}]
UUID{{ $o->entryuuid[0] ?? '' }}
+
+@endsection + +@section('main-content') + @if(session()->has('note')) +
+

Note:

+
+

{{ session()->pull('note') }}

+
+ @endif + + @if(session()->has('success')) +
+

Success!

+
+

{{ session()->pull('success') }}

+
    + @foreach (session()->pull('updated') as $key => $values) +
  • {{ $key }}: {{ join(',',$values) }}
  • + @endforeach +
+
+ @endif + + @if($errors->any()) +
+

Error?

+
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+
+
+
+ + @csrf + + + +

@lang('Do you want to make the following changes?')

+ + + + + + + + + + + @foreach ($o->getDirty() as $key => $value) + + + @for($xx=0;$xx<$x;$xx++) + @if($xx) + + @endif + + + + @endfor + + @endforeach + +
AttributeOLDNEW
{{ $key }}
{{ Arr::get(Arr::get($o->getOriginal(),$key,['['.strtoupper(__('New Value')).']']),$xx) }}{{ $y=Arr::get($value,$xx) }}
+ +
+ +
+
+ @lang('Reset') + @lang('Update') +
+
+
+
+
+@endsection \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index dfe07d4..93bff74 100644 --- a/routes/web.php +++ b/routes/web.php @@ -39,4 +39,6 @@ Route::group(['prefix'=>'user'],function() { Route::get('image',[HomeController::class,'user_image']); }); -Route::post('entry/update',[HomeController::class,'entry_update']); \ No newline at end of file +Route::post('entry/update/commit',[HomeController::class,'entry_update']); +Route::post('entry/update/pending',[HomeController::class,'entry_pending_update']); +Route::get('entry/newattr/{id}',[HomeController::class,'entry_newattr']); \ No newline at end of file