diff --git a/app/Classes/LDAP/Attribute.php b/app/Classes/LDAP/Attribute.php index 2c9fc40..9b027d1 100644 --- a/app/Classes/LDAP/Attribute.php +++ b/app/Classes/LDAP/Attribute.php @@ -2,6 +2,7 @@ namespace App\Classes\LDAP; +use Illuminate\Contracts\View\View; use Illuminate\Support\Collection; use App\Classes\LDAP\Schema\AttributeType; @@ -130,12 +131,14 @@ class Attribute 'hints' => $this->hints(), // Is this an internal attribute 'is_internal' => isset($this->{$key}) && $this->{$key}, + // Is this attribute the RDN + 'is_rdn' => $this->is_rdn, // We prefer the name as per the schema if it exists 'name' => $this->schema ? $this->schema->{$key} : $this->{$key}, // Attribute name in lower case 'name_lc' => strtolower($this->name), - // Is this attribute the RDN - 'rdn' => $this->is_rdn, + // Attribute values + 'values' => $this->values, default => throw new \Exception('Unknown key:' . $key), }; @@ -151,22 +154,6 @@ class Attribute return $this->values->join('
'); } - /** - * Return an instance of this attribute that is deletable. - * This is primarily used for rendering to know if to render delete options. - * - * @return Attribute - */ - public function deletable(): self - { - $clone = clone $this; - - if (! $this->required_by->count()) - $clone->is_deletable = TRUE; - - return $clone; - } - /** * Return the hints about this attribute, ie: RDN, Required, etc * @@ -195,6 +182,13 @@ class Attribute return $result->toArray(); } + public function render(bool $edit): View + { + return view('components.attribute') + ->with('edit',$edit) + ->with('o',$this); + } + /** * Set the objectclasses that require this attribute * diff --git a/app/Http/Controllers/APIController.php b/app/Http/Controllers/APIController.php index 1ce3271..0f85226 100644 --- a/app/Http/Controllers/APIController.php +++ b/app/Http/Controllers/APIController.php @@ -27,7 +27,7 @@ class APIController extends Controller ->transform(function($item) { return [ 'title'=>$item->getRdn(), - 'item'=>Crypt::encryptString($item->getDn()), + 'item'=>$item->getDNSecure(), 'icon'=>$item->icon(), 'lazy'=>Arr::get($item->getAttribute('hassubordinates'),0) == 'TRUE', 'tooltip'=>$item->getDn(), diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 4ecc8a7..6cf7c3b 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -8,10 +8,14 @@ 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 LdapRecord\Exceptions\InsufficientAccessException; +use LdapRecord\LdapRecordException; use LdapRecord\Query\ObjectNotFoundException; use App\Classes\LDAP\Server; use App\Exceptions\InvalidUsage; +use App\Http\Requests\EntryRequest; class HomeController extends Controller { @@ -25,6 +29,66 @@ class HomeController extends Controller return view('debug'); } + /** + * Render a specific DN + * + * @param Request $request + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + */ + public function dn_frame(Request $request) + { + $dn = Crypt::decryptString($request->post('key')); + + return view('frames.dn') + ->with('o',config('server')->fetch($dn)) + ->with('dn',$dn); + } + + public function entry_update(EntryRequest $request) + { + $dn = Crypt::decryptString($request->dn); + + $o = config('server')->fetch($dn); + + foreach ($request->except(['_token','dn']) as $key => $value) + $o->{$key} = array_filter($value); + + Session::put('dn',$request->dn); + + if (! $dirty=$o->getDirty()) + return back()->with(['note'=>__('No attributes changed')]); + + try { + $o->update($request->except(['_token','dn'])); + + } catch (InsufficientAccessException $e) { + $request->flash(); + + switch ($x=$e->getDetailedError()->getErrorCode()) { + case 50: + return back()->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage()))); + + default: + abort(599,$e->getDetailedError()->getErrorMessage()); + } + + } catch (LdapRecordException $e) { + $request->flash(); + + switch ($x=$e->getDetailedError()->getErrorCode()) { + case 8: + return back()->withErrors(sprintf('%s: %s (%s)',__('LDAP Server Error Code'),$x,__($e->getDetailedError()->getErrorMessage()))); + + default: + abort(599,$e->getDetailedError()->getErrorMessage()); + } + } + + return back() + ->with(['success'=>__('Entry updated')]) + ->with(['updated'=>$dirty]); + } + /** * Application home page */ @@ -32,17 +96,25 @@ class HomeController extends Controller { $base = Server::baseDNs() ?: collect(); - return view('home') - ->with('server',config('ldap.connections.default.name')) - ->with('bases',$base->transform(function($item) { - return [ - 'title'=>$item->getRdn(), - 'item'=>Crypt::encryptString($item->getDn()), - 'lazy'=>TRUE, - 'icon'=>'fa-fw fas fa-sitemap', - 'tooltip'=>$item->getDn(), - ]; - })); + $bases = $base->transform(function($item) { + return [ + 'title'=>$item->getRdn(), + 'item'=>$item->getDNSecure(), + 'lazy'=>TRUE, + 'icon'=>'fa-fw fas fa-sitemap', + 'tooltip'=>$item->getDn(), + ]; + }); + + if (Session::has('dn')) + return view('dn') + ->with('bases',$bases) + ->with('o',config('server')->fetch($dn=Crypt::decryptString(Session::pull('dn')))) + ->with('dn',$dn); + else + return view('home') + ->with('bases',$bases) + ->with('server',config('ldap.connections.default.name')); } /** @@ -62,21 +134,6 @@ class HomeController extends Controller ->with('s',$s); } - /** - * Render a specific DN - * - * @param Request $request - * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View - */ - public function dn_frame(Request $request) - { - $dn = Crypt::decryptString($request->post('key')); - - return view('frames.dn') - ->with('o',config('server')->fetch($dn)) - ->with('dn',$dn); - } - /** * Show the Schema Viewer * diff --git a/app/Http/Requests/EntryRequest.php b/app/Http/Requests/EntryRequest.php new file mode 100644 index 0000000..a57b07e --- /dev/null +++ b/app/Http/Requests/EntryRequest.php @@ -0,0 +1,31 @@ + + */ + public function rules() + { + return [ + 'dn'=>'string|min:3', + 'objectclass'=>'array|min:1', + ]; + } +} \ No newline at end of file diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index eca3366..18288c2 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -4,6 +4,7 @@ namespace App\Ldap; use Illuminate\Support\Arr; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Crypt; use LdapRecord\Models\Model; use App\Classes\LDAP\Attribute; @@ -93,6 +94,15 @@ class Entry extends Model /* METHODS */ + /** + * Return a secure version of the DN + * @return string + */ + public function getDNSecure(): string + { + return Crypt::encryptString($this->getDn()); + } + /** * Return a list of LDAP internal attributes * diff --git a/app/View/Components/Attribute.php b/app/View/Components/Attribute.php new file mode 100644 index 0000000..ff40a6b --- /dev/null +++ b/app/View/Components/Attribute.php @@ -0,0 +1,34 @@ +edit = $edit; + $this->o = $o; + } + + /** + * Get the view / contents that represent the component. + * + * @return \Illuminate\Contracts\View\View|\Closure|string + */ + public function render() + { + return $this->o->render($this->edit); + } +} \ No newline at end of file diff --git a/public/js/custom.js b/public/js/custom.js index 18249cc..e3ecf03 100644 --- a/public/js/custom.js +++ b/public/js/custom.js @@ -11,6 +11,38 @@ function expandChildren(node) { } } +function getNode(item) { + $.ajax({ + url: 'dn', + method: 'POST', + data: { key: item }, + dataType: 'html', + beforeSend: function() { + content = $('.main-content').contents(); + $('.main-content').empty().append('
'); + } + + }).done(function(html) { + $('.main-content').empty().append(html); + + }).fail(function(item) { + switch(item.status) { + case 404: + $('.main-content').empty().append(item.responseText); + break; + case 419: + alert('Session has expired, reloading the page and try again...'); + location.reload(); + break; + case 500: + $('.main-content').empty().append(item.responseText); + break; + default: + alert(item.status+': Well that didnt work?'); + } + }); +} + $(document).ready(function() { // If our bases have been set, we'll render them directly if (typeof basedn !== 'undefined') { @@ -50,35 +82,7 @@ $(document).ready(function() { }, click: function(event,data) { if (data.targetType == 'title') { - $.ajax({ - url: 'dn', - method: 'POST', - data: { key: data.node.data.item }, - dataType: 'html', - beforeSend: function() { - content = $('.main-content').contents(); - $('.main-content').empty().append('
'); - } - - }).done(function(html) { - $('.main-content').empty().append(html); - - }).fail(function(item) { - switch(item.status) { - case 404: - $('.main-content').empty().append(item.responseText); - break; - case 419: - alert('Session has expired, reloading the page and try again...'); - location.reload(); - break; - case 500: - $('.main-content').empty().append(item.responseText); - break; - default: - alert(item.status+': Well that didnt work?'); - } - }); + getNode(data.node.data.item); } }, source: sources, @@ -90,13 +94,16 @@ $(document).ready(function() { expandChildren(data.tree.rootNode); }, - keydown: function(event, data){ + keydown: function(event,data){ switch( $.ui.fancytree.eventToString(data.originalEvent) ) { case 'return': case 'space': data.node.toggleExpanded(); break; } + }, + restore: function(event,data) { + //getNode(data.tree.getActiveNode().data.item); } }); }); \ No newline at end of file diff --git a/resources/themes/architect/views/layouts/error.blade.php b/resources/themes/architect/views/layouts/error.blade.php index c4408b3..ab30ef4 100644 --- a/resources/themes/architect/views/layouts/error.blade.php +++ b/resources/themes/architect/views/layouts/error.blade.php @@ -15,9 +15,9 @@