From 4f9accbadf296921d2b378dfce30639aa59d9bdd Mon Sep 17 00:00:00 2001 From: Deon George Date: Sun, 19 Feb 2023 16:35:07 +1100 Subject: [PATCH] Move some server function to Server::class (from Entry::class) --- VERSION | 2 +- app/Classes/LDAP/Server.php | 203 ++++++++++++++++++++++-- app/Http/Controllers/HomeController.php | 57 +++---- app/Ldap/Entry.php | 170 -------------------- app/Ldap/LdapUserRepository.php | 6 +- resources/views/debug.blade.php | 4 +- resources/views/frames/dn.blade.php | 2 +- resources/views/frames/info.blade.php | 27 ++++ resources/views/frames/schema.blade.php | 2 +- tests/Unit/GetBaseDNTest.php | 6 +- tests/Unit/TranslateOidTest.php | 6 +- 11 files changed, 258 insertions(+), 227 deletions(-) create mode 100644 resources/views/frames/info.blade.php diff --git a/VERSION b/VERSION index 8b13789..34725a9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ - +GIT diff --git a/app/Classes/LDAP/Server.php b/app/Classes/LDAP/Server.php index 7a04ea8..3ca77b3 100644 --- a/app/Classes/LDAP/Server.php +++ b/app/Classes/LDAP/Server.php @@ -5,11 +5,14 @@ namespace App\Classes\LDAP; use Carbon\Carbon; use Exception; use Illuminate\Support\Arr; -use Illuminate\Support\Collection as ArrayCollection; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Log; -use LdapRecord\Query\Collection; +use LdapRecord\LdapRecordException; +use LdapRecord\Models\Model; +use LdapRecord\Query\Collection as LDAPCollection; +use LdapRecord\Query\ObjectNotFoundException; use App\Classes\LDAP\Schema\{AttributeType,Base,LDAPSyntax,MatchingRule,MatchingRuleUse,ObjectClass}; use App\Exceptions\InvalidUsage; @@ -18,11 +21,11 @@ use App\Ldap\Entry; class Server { // This servers schema objectclasses - private ArrayCollection $attributetypes; - private ArrayCollection $ldapsyntaxes; - private ArrayCollection $matchingrules; - private ArrayCollection $matchingruleuse; - private ArrayCollection $objectclasses; + private Collection $attributetypes; + private Collection $ldapsyntaxes; + private Collection $matchingrules; + private Collection $matchingruleuse; + private Collection $objectclasses; // Valid items that can be fetched public const schema_types = [ @@ -45,13 +48,187 @@ class Server } } + /* STATIC METHODS */ + + /** + * Gets the root DN of the specified LDAPServer, or throws an exception if it + * can't find it. + * + * @param null $connection Return a collection of baseDNs + * @param bool $objects Return a collection of Entry Models + * @return Collection + * @throws ObjectNotFoundException + * @testedin GetBaseDNTest::testBaseDNExists(); + */ + public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection + { + $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); + + try { + $base = self::rootDSE($connection,$cachetime); + + /** + * LDAP Error Codes: + * https://ldap.com/ldap-result-code-reference/ + * + success 0 + * + operationsError 1 + * + protocolError 2 + * + timeLimitExceeded 3 + * + sizeLimitExceeded 4 + * + compareFalse 5 + * + compareTrue 6 + * + authMethodNotSupported 7 + * + strongerAuthRequired 8 + * + referral 10 + * + adminLimitExceeded 11 + * + unavailableCriticalExtension 12 + * + confidentialityRequired 13 + * + saslBindInProgress 14 + * + noSuchAttribute 16 + * + undefinedAttributeType 17 + * + inappropriateMatching 18 + * + constraintViolation 19 + * + attributeOrValueExists 20 + * + invalidAttributeSyntax 21 + * + noSuchObject 32 + * + aliasProblem 33 + * + invalidDNSyntax 34 + * + isLeaf 35 + * + aliasDereferencingProblem 36 + * + inappropriateAuthentication 48 + * + invalidCredentials 49 + * + insufficientAccessRights 50 + * + busy 51 + * + unavailable 52 + * + unwillingToPerform 53 + * + loopDetect 54 + * + sortControlMissing 60 + * + offsetRangeError 61 + * + namingViolation 64 + * + objectClassViolation 65 + * + notAllowedOnNonLeaf 66 + * + notAllowedOnRDN 67 + * + entryAlreadyExists 68 + * + objectClassModsProhibited 69 + * + resultsTooLarge 70 + * + affectsMultipleDSAs 71 + * + virtualListViewError or controlError 76 + * + other 80 + * + serverDown 81 + * + localError 82 + * + encodingError 83 + * + decodingError 84 + * + timeout 85 + * + authUnknown 86 + * + filterError 87 + * + userCanceled 88 + * + paramError 89 + * + noMemory 90 + * + connectError 91 + * + notSupported 92 + * + controlNotFound 93 + * + noResultsReturned 94 + * + moreResultsToReturn 95 + * + clientLoop 96 + * + referralLimitExceeded 97 + * + invalidResponse 100 + * + ambiguousResponse 101 + * + tlsNotSupported 112 + * + intermediateResponse 113 + * + unknownType 114 + * + canceled 118 + * + noSuchOperation 119 + * + tooLate 120 + * + cannotCancel 121 + * + assertionFailed 122 + * + authorizationDenied 123 + * + e-syncRefreshRequired 4096 + * + noOperation 16654 + * + * LDAP Tag Codes: + * + A client bind operation 97 + * + The entry for which you were searching 100 + * + The result from a search operation 101 + * + The result from a modify operation 103 + * + The result from an add operation 105 + * + The result from a delete operation 107 + * + The result from a modify DN operation 109 + * + The result from a compare operation 111 + * + A search reference when the entry you perform your search on holds a referral to the entry you require. + * + Search references are expressed in terms of a referral. + * 115 + * + A result from an extended operation 120 + */ + // If we cannot get to our LDAP server we'll head straight to the error page + } catch (LdapRecordException $e) { + switch ($e->getDetailedError()->getErrorCode()) { + case 49: + abort(401,$e->getDetailedError()->getErrorMessage()); + + default: + abort(597,$e->getDetailedError()->getErrorMessage()); + } + } + + if (! $objects) + return collect($base->namingcontexts); + + /** + * @note While we are caching our baseDNs, it seems if we have more than 1, + * our caching doesnt generate a hit on a subsequent call to this function (before the cache expires). + * IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all. + * @todo Possibly a bug wtih ldaprecord, so need to investigate + */ + $result = collect(); + foreach ($base->namingcontexts as $dn) { + $result->push((new Entry)->cache($cachetime)->findOrFail($dn)); + } + + return $result; + } + + /** + * Obtain the rootDSE for the server, that gives us server information + * + * @param null $connection + * @return Entry|null + * @throws ObjectNotFoundException + * @testedin TranslateOidTest::testRootDSE(); + */ + public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model + { + $e = new Entry; + + return Entry::on($connection ?? $e->getConnectionName()) + ->cache($cachetime) + ->in(NULL) + ->read() + ->select(['+']) + ->whereHas('objectclass') + ->firstOrFail(); + } + + /** + * Get the Schema DN + * + * @param $connection + * @return string + * @throws ObjectNotFoundException + */ + public static function schemaDN($connection=NULL): string + { + $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); + + return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first(); + } + /** * Query the server for a DN and return its children and if those children have children. * * @param string $dn - * @return Collection|null + * @return LDAPCollection|NULL */ - public function children(string $dn): ?Collection + public function children(string $dn): ?LDAPCollection { return ($x=(new Entry) ->query() @@ -150,10 +327,10 @@ class Server * * @param string $item Schema Item to Fetch * @param string|null $key - * @return ArrayCollection|Base + * @return Collection|Base|NULL * @throws InvalidUsage */ - public function schema(string $item,string $key=NULL): ArrayCollection|Base|NULL + public function schema(string $item,string $key=NULL): Collection|Base|NULL { // Ensure our item to fetch is lower case $item = strtolower($item); @@ -215,8 +392,8 @@ class Server } // Try to get the schema DN from the specified entry. - $schema_dn = Entry::schemaDN(); - $schema = (new Server)->fetch($schema_dn); + $schema_dn = $this->schemaDN(); + $schema = $this->fetch($schema_dn); switch ($item) { case 'attributetypes': diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 4217c92..d1086e5 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -10,8 +10,8 @@ use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\File; use LdapRecord\Query\ObjectNotFoundException; -use App\Ldap\Entry; use App\Classes\LDAP\Server; +use App\Exceptions\InvalidUsage; class HomeController extends Controller { @@ -30,7 +30,7 @@ class HomeController extends Controller */ public function home() { - $base = (new Entry)->baseDNs() ?: collect(); + $base = Server::baseDNs() ?: collect(); return view('home') ->with('server',config('ldap.connections.default.name')) @@ -53,34 +53,13 @@ class HomeController extends Controller */ public function info() { - $root = (new Entry)->rootDSE(); + // Load our attributes + $s = new Server; + $s->schema('objectclasses'); + $s->schema('attributetypes'); - try { - $attrs = collect($root->getAttributes()) - ->transform(function($item) { - foreach ($item as $k=>$v) { - if (preg_match('/[0-9]+\.[0-9]+\.[0-9]+/',$v)) { - $format = sprintf( - '%s%s

%s

', - $v, - Server::getOID($v,'title'), - ($x=Server::getOID($v,'ref')) ? sprintf('',$x) : '', - Server::getOID($v,'desc'), - ); - $item[$k] = $format; - } - } - return $item; - }); - - // @todo If we cant get server info, we should probably show a nice error dialog - } catch (ObjectNotFoundException $e) { - $attrs = collect(); - } - - return view('frames.dn') - ->with('o',$root) - ->with('dn',__('Server Info')); + return view('frames.info') + ->with('s',$s); } /** @@ -98,9 +77,25 @@ class HomeController extends Controller ->with('dn',$dn); } - public function schema_frame() + /** + * Show the Schema Viewer + * + * @note Our route will validate that types are valid. + * @param Request $request + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View + * @throws InvalidUsage + */ + public function schema_frame(Request $request) { - return view('frames.schema'); + $s = new Server; + + // If an invalid key, we'll 404 + if ($request->type && $request->key && ($s->schema($request->type)->has($request->key) === FALSE)) + abort(404); + + return view('frames.schema') + ->with('type',$request->type) + ->with('key',$request->key); } /** diff --git a/app/Ldap/Entry.php b/app/Ldap/Entry.php index 76ba501..b05dfda 100644 --- a/app/Ldap/Entry.php +++ b/app/Ldap/Entry.php @@ -2,13 +2,8 @@ namespace App\Ldap; -use Carbon\Carbon; use Illuminate\Support\Arr; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Config; -use LdapRecord\LdapRecordException; use LdapRecord\Models\Model; -use LdapRecord\Query\ObjectNotFoundException; use App\Classes\LDAP\Attribute\Factory; @@ -26,171 +21,6 @@ class Entry extends Model return $result->toArray(); } - /* STATIC METHODS */ - - /** - * Gets the root DN of the specified LDAPServer, or throws an exception if it - * can't find it. - * - * @param null $connection Return a collection of baseDNs - * @param bool $objects Return a collection of Entry Models - * @return Collection - * @throws ObjectNotFoundException - * @testedin GetBaseDNTest::testBaseDNExists(); - */ - public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection - { - $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); - - try { - $base = self::rootDSE($connection,$cachetime); - - /** - * LDAP Error Codes: - * https://ldap.com/ldap-result-code-reference/ - * + success 0 - * + operationsError 1 - * + protocolError 2 - * + timeLimitExceeded 3 - * + sizeLimitExceeded 4 - * + compareFalse 5 - * + compareTrue 6 - * + authMethodNotSupported 7 - * + strongerAuthRequired 8 - * + referral 10 - * + adminLimitExceeded 11 - * + unavailableCriticalExtension 12 - * + confidentialityRequired 13 - * + saslBindInProgress 14 - * + noSuchAttribute 16 - * + undefinedAttributeType 17 - * + inappropriateMatching 18 - * + constraintViolation 19 - * + attributeOrValueExists 20 - * + invalidAttributeSyntax 21 - * + noSuchObject 32 - * + aliasProblem 33 - * + invalidDNSyntax 34 - * + isLeaf 35 - * + aliasDereferencingProblem 36 - * + inappropriateAuthentication 48 - * + invalidCredentials 49 - * + insufficientAccessRights 50 - * + busy 51 - * + unavailable 52 - * + unwillingToPerform 53 - * + loopDetect 54 - * + sortControlMissing 60 - * + offsetRangeError 61 - * + namingViolation 64 - * + objectClassViolation 65 - * + notAllowedOnNonLeaf 66 - * + notAllowedOnRDN 67 - * + entryAlreadyExists 68 - * + objectClassModsProhibited 69 - * + resultsTooLarge 70 - * + affectsMultipleDSAs 71 - * + virtualListViewError or controlError 76 - * + other 80 - * + serverDown 81 - * + localError 82 - * + encodingError 83 - * + decodingError 84 - * + timeout 85 - * + authUnknown 86 - * + filterError 87 - * + userCanceled 88 - * + paramError 89 - * + noMemory 90 - * + connectError 91 - * + notSupported 92 - * + controlNotFound 93 - * + noResultsReturned 94 - * + moreResultsToReturn 95 - * + clientLoop 96 - * + referralLimitExceeded 97 - * + invalidResponse 100 - * + ambiguousResponse 101 - * + tlsNotSupported 112 - * + intermediateResponse 113 - * + unknownType 114 - * + canceled 118 - * + noSuchOperation 119 - * + tooLate 120 - * + cannotCancel 121 - * + assertionFailed 122 - * + authorizationDenied 123 - * + e-syncRefreshRequired 4096 - * + noOperation 16654 - * - * LDAP Tag Codes: - * + A client bind operation 97 - * + The entry for which you were searching 100 - * + The result from a search operation 101 - * + The result from a modify operation 103 - * + The result from an add operation 105 - * + The result from a delete operation 107 - * + The result from a modify DN operation 109 - * + The result from a compare operation 111 - * + A search reference when the entry you perform your search on holds a referral to the entry you require. - * + Search references are expressed in terms of a referral. - * 115 - * + A result from an extended operation 120 - */ - // If we cannot get to our LDAP server we'll head straight to the error page - } catch (LdapRecordException $e) { - switch ($e->getDetailedError()->getErrorCode()) { - case 49: - abort(401,$e->getDetailedError()->getErrorMessage()); - - default: - abort(597,$e->getDetailedError()->getErrorMessage()); - } - } - - if (! $objects) - return collect($base->namingcontexts); - - /** - * @note While we are caching our baseDNs, it seems if we have more than 1, - * our caching doesnt generate a hit on a subsequent call to this function (before the cache expires). - * IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all. - * @todo Possibly a bug wtih ldaprecord, so need to investigate - */ - $result = collect(); - foreach ($base->namingcontexts as $dn) { - $result->push((new self)->cache($cachetime)->findOrFail($dn)); - } - - return $result; - } - - /** - * Obtain the rootDSE for the server, that gives us server information - * - * @param null $connection - * @return Entry|null - * @throws ObjectNotFoundException - * @testedin TranslateOidTest::testRootDSE(); - */ - public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model - { - return static::on($connection ?? (new static)->getConnectionName()) - ->cache($cachetime) - ->in(NULL) - ->read() - ->select(['+']) - ->whereHas('objectclass') - ->firstOrFail(); - } - - public static function schemaDN($connection = NULL): string - { - $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); - - return collect(self::rootDSE($connection,$cachetime)->subschemasubentry)->first(); - } - /* ATTRIBUTES */ /** diff --git a/app/Ldap/LdapUserRepository.php b/app/Ldap/LdapUserRepository.php index 990a00a..ced1017 100644 --- a/app/Ldap/LdapUserRepository.php +++ b/app/Ldap/LdapUserRepository.php @@ -8,6 +8,8 @@ use LdapRecord\Laravel\Events\Auth\DiscoveredWithCredentials; use LdapRecord\Laravel\LdapUserRepository as LdapUserRepositoryBase; use LdapRecord\Models\Model; +use App\Classes\LDAP\Server; + class LdapUserRepository extends LdapUserRepositoryBase { /** @@ -25,7 +27,7 @@ class LdapUserRepository extends LdapUserRepositoryBase } // Look for a user using all our baseDNs - foreach ((new Entry)->baseDNs() as $base) { + foreach (Server::baseDNs() as $base) { $query = $this->query()->setBaseDn($base); foreach ($credentials as $key => $value) { @@ -61,7 +63,7 @@ class LdapUserRepository extends LdapUserRepositoryBase public function findByGuid($guid): ?Model { // Look for a user using all our baseDNs - foreach ((new Entry)->baseDNs() as $base) { + foreach (Server::baseDNs() as $base) { $user = $this->query()->setBaseDn($base)->findByGuid($guid); if ($user) diff --git a/resources/views/debug.blade.php b/resources/views/debug.blade.php index 04e1116..7032089 100644 --- a/resources/views/debug.blade.php +++ b/resources/views/debug.blade.php @@ -24,7 +24,7 @@ BaseDN(s) - @foreach(\App\Ldap\Entry::baseDNs()->sort(function($item) { return $item->sortKey; }) as $item) + @foreach(\App\Classes\LDAP\Server::baseDNs()->sort(function($item) { return $item->sortKey; }) as $item) @@ -36,7 +36,7 @@ - +
{{ $item->getDn() }}
Schema DN{{ \App\Ldap\Entry::schemaDN() }}{{ \App\Classes\LDAP\Server::schemaDN() }}
diff --git a/resources/views/frames/dn.blade.php b/resources/views/frames/dn.blade.php index 8cfb191..9a474a9 100644 --- a/resources/views/frames/dn.blade.php +++ b/resources/views/frames/dn.blade.php @@ -14,7 +14,7 @@ @foreach ($o->getAttributes() as $attribute => $value) - + @endforeach diff --git a/resources/views/frames/info.blade.php b/resources/views/frames/info.blade.php new file mode 100644 index 0000000..b8d7e4b --- /dev/null +++ b/resources/views/frames/info.blade.php @@ -0,0 +1,27 @@ +@extends('layouts.dn') + +@section('page_title') +
{{ $attribute }}{{ $attribute }} {!! $value !!}
+ + + + +
{{ __('Server Info') }}
{{ $s->rootDSE()->entryuuid[0] ?? '' }}
+@endsection + +@section('main-content') +
+ + @foreach ($s->rootDSE()->getAttributes() as $attribute => $value) + + + + + @endforeach +
+ {!! ($x=$s->schema('attributetypes',$attribute)) + ? sprintf('%s',$x->name_lc,url('schema/attributetypes',$x->name_lc),$x->name) + : $attribute !!} + {!! $value !!}
+
+@endsection \ No newline at end of file diff --git a/resources/views/frames/schema.blade.php b/resources/views/frames/schema.blade.php index 012023b..5279ad7 100644 --- a/resources/views/frames/schema.blade.php +++ b/resources/views/frames/schema.blade.php @@ -4,7 +4,7 @@ - +
{{ \App\Ldap\Entry::schemaDN() }}
{{ $o->entryuuid[0] ?? '' }}
{{ \App\Classes\LDAP\Server::schemaDN() }}
@endsection diff --git a/tests/Unit/GetBaseDNTest.php b/tests/Unit/GetBaseDNTest.php index d7ceffd..defd6fa 100644 --- a/tests/Unit/GetBaseDNTest.php +++ b/tests/Unit/GetBaseDNTest.php @@ -4,7 +4,7 @@ namespace Tests\Unit; use Tests\TestCase; -use App\Ldap\Entry; +use App\Classes\LDAP\Server; class GetBaseDNTest extends TestCase { @@ -13,11 +13,11 @@ class GetBaseDNTest extends TestCase * * @return void * @throws \LdapRecord\Query\ObjectNotFoundException - * @covers \App\Ldap\Entry::baseDNs() + * @covers \App\Classes\LDAP\Server::baseDNs() */ public function testBaseDnExists() { - $o = (new Entry)->baseDNs(); + $o = Server::baseDNs(); $this->assertIsObject($o); $this->assertCount(6,$o->toArray()); diff --git a/tests/Unit/TranslateOidTest.php b/tests/Unit/TranslateOidTest.php index 4dca3b5..ef43c68 100644 --- a/tests/Unit/TranslateOidTest.php +++ b/tests/Unit/TranslateOidTest.php @@ -2,10 +2,10 @@ namespace Tests\Unit; +use LdapRecord\Query\ObjectNotFoundException; use Tests\TestCase; use App\Classes\LDAP\Server; -use App\Ldap\Entry; class TranslateOidTest extends TestCase { @@ -13,12 +13,12 @@ class TranslateOidTest extends TestCase * A basic feature test example. * * @return void - * @throws \LdapRecord\Query\ObjectNotFoundException * @covers \App\Classes\LDAP\Server::getOID() + * @throws ObjectNotFoundException */ public function testRootDse() { - $dse = (new Entry)->rootDSE(); + $dse = Server::rootDSE(); // Test our rootDSE returns an objectclass attribute $this->assertIsArray($dse->objectclass);