diff --git a/app/Classes/LDAP/Schema/ObjectClass.php b/app/Classes/LDAP/Schema/ObjectClass.php index 136c527..f06b424 100644 --- a/app/Classes/LDAP/Schema/ObjectClass.php +++ b/app/Classes/LDAP/Schema/ObjectClass.php @@ -2,12 +2,12 @@ namespace App\Classes\LDAP\Schema; +use Config; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use App\Classes\LDAP\Server; use App\Exceptions\InvalidUsage; -use App\Ldap\Entry; /** * Represents an LDAP Schema objectClass @@ -15,10 +15,8 @@ use App\Ldap\Entry; * @package phpLDAPadmin * @subpackage Schema */ -final class ObjectClass extends Base { - // The server ID that this objectclass belongs to. - private Server $server; - +final class ObjectClass extends Base +{ // Array of objectClass names from which this objectClass inherits private Collection $sup_classes; @@ -39,15 +37,14 @@ final class ObjectClass extends Base { private bool $is_obsolete; - /* ObjectClass Types */ - private const OC_STRUCTURAL = 0x01; - private const OC_ABSTRACT = 0x02; - private const OC_AUXILIARY = 0x03; - /** * Creates a new ObjectClass object given a raw LDAP objectClass string. * * eg: ( 2.5.6.0 NAME 'top' DESC 'top of the superclass chain' ABSTRACT MUST objectClass ) + * + * @param string $line Schema Line + * @param Server $server + * @todo Change $server to $connection, no need to store the server object here */ public function __construct(string $line,Server $server) { @@ -59,7 +56,6 @@ final class ObjectClass extends Base { $strings = preg_split('/[\s,]+/',$line,-1,PREG_SPLIT_DELIM_CAPTURE); // Init - $this->server = $server; $this->may_attrs = collect(); $this->may_force = collect(); $this->must_attrs = collect(); @@ -138,21 +134,21 @@ final class ObjectClass extends Base { break; case 'ABSTRACT': - $this->type = self::OC_ABSTRACT; + $this->type = Server::OC_ABSTRACT; if (static::DEBUG_VERBOSE) Log::debug(sprintf('- Case ABSTRACT returned (%s)',$this->type)); break; case 'STRUCTURAL': - $this->type = self::OC_STRUCTURAL; + $this->type = Server::OC_STRUCTURAL; if (static::DEBUG_VERBOSE) Log::debug(sprintf('- Case STRUCTURAL returned (%s)',$this->type)); break; case 'AUXILIARY': - $this->type = self::OC_AUXILIARY; + $this->type = Server::OC_AUXILIARY; if (static::DEBUG_VERBOSE) Log::debug(sprintf('- Case AUXILIARY returned (%s)',$this->type)); @@ -212,34 +208,29 @@ final class ObjectClass extends Base { public function __get(string $key): mixed { - switch ($key) { - case 'attributes': - return $this->getAllAttrs(); - - case 'sup': - return $this->sup_classes; - - case 'type_name': - switch ($this->type) { - case self::OC_STRUCTURAL: return 'Structural'; - case self::OC_ABSTRACT: return 'Abstract'; - case self::OC_AUXILIARY: return 'Auxiliary'; - default: - throw new InvalidUsage('Unknown ObjectClass Type: '.$this->type); - } - - default: return parent::__get($key); - } + return match ($key) { + 'attributes' => $this->getAllAttrs(), + 'sup' => $this->sup_classes, + 'type_name' => match ($this->type) { + Server::OC_STRUCTURAL => 'Structural', + Server::OC_ABSTRACT => 'Abstract', + Server::OC_AUXILIARY => 'Auxiliary', + default => throw new InvalidUsage('Unknown ObjectClass Type: ' . $this->type), + }, + default => parent::__get($key), + }; } /** * Return a list of attributes that this objectClass provides * * @return Collection + * @throws InvalidUsage */ public function getAllAttrs(): Collection { - return $this->getMustAttrs()->merge($this->getMayAttrs()); + return $this->getMustAttrs() + ->merge($this->getMayAttrs()); } /** @@ -250,9 +241,8 @@ final class ObjectClass extends Base { */ public function addChildObjectClass(string $name): void { - if ($this->child_objectclasses->search($name) === FALSE) { + if (! $this->child_objectclasses->has($name)) $this->child_objectclasses->push($name); - } } /** @@ -321,14 +311,13 @@ final class ObjectClass extends Base { { // If we dont need our parents, then we'll just return ours. if (! $parents) - return $this->may_attrs->sortBy(function($item) { return strtolower($item->name.$item->source); }); + return $this->may_attrs + ->sortBy(fn($item)=>strtolower($item->name.$item->source)); $attrs = $this->may_attrs; - foreach ($this->getParents() as $object_class) { - $sc = $this->server->schema('objectclasses',$object_class); - $attrs = $attrs->merge($sc->getMayAttrs($parents)); - } + foreach ($this->getParents() as $object_class) + $attrs = $attrs->merge($object_class->getMayAttrs($parents)); // Remove any duplicates $attrs = $attrs->unique(function($item) { return $item->name; }); @@ -378,10 +367,8 @@ final class ObjectClass extends Base { $attrs = $this->must_attrs; - foreach ($this->getParents() as $object_class) { - $sc = $this->server->schema('objectclasses',$object_class); - $attrs = $attrs->merge($sc->getMustAttrs($parents)); - } + foreach ($this->getParents() as $object_class) + $attrs = $attrs->merge($object_class->getMustAttrs($parents)); // Remove any duplicates $attrs = $attrs->unique(function($item) { return $item->name; }); @@ -423,12 +410,13 @@ final class ObjectClass extends Base { $result = collect(); foreach ($this->sup_classes as $object_class) { - $result->push($object_class); + $oc = Config::get('server') + ->schema('objectclasses',$object_class); - $oc = $this->server->schema('objectclasses',$object_class); - - if ($oc) + if ($oc) { + $result->push($oc); $result = $result->merge($oc->getParents()); + } } return $result; @@ -476,19 +464,16 @@ final class ObjectClass extends Base { if (in_array_ignore_case($this->name,$oclass)) return FALSE; - foreach ($oclass as $object_class) { - $oc = $this->server->schema('objectclasses',$object_class); - - if ($oc->isStructural() && in_array_ignore_case($this->name,$oc->getParents())) + foreach ($oclass as $object_class) + if ($object_class->isStructural() && in_array_ignore_case($this->name,$object_class->getParents()->pluck('name'))) return TRUE; - } return FALSE; } public function isStructural(): bool { - return $this->type === self::OC_STRUCTURAL; + return $this->type === Server::OC_STRUCTURAL; } /** diff --git a/app/Classes/LDAP/Server.php b/app/Classes/LDAP/Server.php index c27a030..81b7553 100644 --- a/app/Classes/LDAP/Server.php +++ b/app/Classes/LDAP/Server.php @@ -21,6 +21,9 @@ use App\Ldap\Entry; final class Server { + // Connection information used for these object and children + private ?string $connection; + // This servers schema objectclasses private Collection $attributetypes; private Collection $ldapsyntaxes; @@ -28,25 +31,26 @@ final class Server private Collection $matchingruleuse; private Collection $objectclasses; - // Valid items that can be fetched - public const schema_types = [ - 'objectclasses', - 'attributetypes', - 'ldapsyntaxes', - 'matchingrules', - ]; + /* ObjectClass Types */ + public const OC_STRUCTURAL = 0x01; + public const OC_ABSTRACT = 0x02; + public const OC_AUXILIARY = 0x03; + + public function __construct(string $connection=NULL) + { + $this->connection = $connection; + } public function __get(string $key): mixed { - switch ($key) { - case 'attributetypes': return $this->attributetypes; - case 'ldapsyntaxes': return $this->ldapsyntaxes; - case 'matchingrules': return $this->matchingrules; - case 'objectclasses': return $this->objectclasses; - - default: - throw new Exception('Unknown key:'.$key); - } + return match($key) { + 'attributetypes' => $this->attributetypes, + 'connection' => $this->connection, + 'ldapsyntaxes' => $this->ldapsyntaxes, + 'matchingrules' => $this->matchingrules, + 'objectclasses' => $this->objectclasses, + default => throw new Exception('Unknown key:' . $key), + }; } /* STATIC METHODS */ @@ -62,9 +66,10 @@ final class Server * @testedin GetBaseDNTest::testBaseDNExists(); * @todo Need to allow for the scenario if the baseDN is not readable by ACLs */ - public static function baseDNs($connection=NULL,bool $objects=TRUE): Collection + public static function baseDNs(string $connection='default',bool $objects=TRUE): Collection { - $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); + $cachetime = Carbon::now() + ->addSeconds(Config::get('ldap.cache.time')); try { $base = self::rootDSE($connection,$cachetime); @@ -163,7 +168,7 @@ final class Server */ // If we cannot get to our LDAP server we'll head straight to the error page } catch (LdapRecordException $e) { - switch ($e->getDetailedError()->getErrorCode()) { + switch ($e->getDetailedError()?->getErrorCode()) { case 49: // Since we failed authentication, we should delete our auth cookie if (Cookie::has('password_encrypt')) { @@ -178,7 +183,7 @@ final class Server abort(401,$e->getDetailedError()->getErrorMessage()); default: - abort(597,$e->getDetailedError()->getErrorMessage()); + abort(597,$e->getDetailedError()?->getErrorMessage() ?: $e->getMessage()); } } @@ -192,9 +197,8 @@ final class Server * @todo Possibly a bug wtih ldaprecord, so need to investigate */ $result = collect(); - foreach ($base->namingcontexts as $dn) { + foreach ($base->namingcontexts as $dn) $result->push((new Entry)->cache($cachetime)->findOrFail($dn)); - } return $result; } @@ -207,7 +211,7 @@ final class Server * @throws ObjectNotFoundException * @testedin TranslateOidTest::testRootDSE(); */ - public static function rootDSE($connection=NULL,Carbon $cachetime=NULL): ?Model + public static function rootDSE(string $connection=NULL,Carbon $cachetime=NULL): ?Model { $e = new Entry; @@ -227,7 +231,7 @@ final class Server * @return string * @throws ObjectNotFoundException */ - public static function schemaDN($connection=NULL): string + public static function schemaDN(string $connection=NULL): string { $cachetime = Carbon::now()->addSeconds(Config::get('ldap.cache.time')); @@ -243,7 +247,7 @@ final class Server public function children(string $dn): ?LDAPCollection { return ($x=(new Entry) - ->query() + ->on($this->connection) ->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time'))) ->select(['*','hassubordinates']) ->setDn($dn) @@ -261,7 +265,7 @@ final class Server public function fetch(string $dn,array $attrs=['*','+']): ?Entry { return ($x=(new Entry) - ->query() + ->on($this->connection) ->cache(Carbon::now()->addSeconds(Config::get('ldap.cache.time'))) ->select($attrs) ->find($dn)) ? $x : NULL; @@ -298,17 +302,13 @@ final class Server * @return Collection|Base|NULL * @throws InvalidUsage */ - public function schema(string $item,string $key=NULL): Collection|Base|NULL + public function schema(string $item,string $key=NULL): Collection|LDAPSyntax|Base|NULL { // Ensure our item to fetch is lower case $item = strtolower($item); if ($key) $key = strtolower($key); - // This error message is not localized as only developers should ever see it - if (! in_array($item,self::schema_types)) - throw new InvalidUsage('Invalid request to fetch schema: '.$item); - $result = Cache::remember('schema'.$item,config('ldap.cache.time'),function() use ($item) { // First pass if we have already retrieved the schema item switch ($item) { @@ -354,13 +354,13 @@ final class Server break; - // Shouldnt get here + // This error message is not localized as only developers should ever see it default: throw new InvalidUsage('Invalid request to fetch schema: '.$item); } // Try to get the schema DN from the specified entry. - $schema_dn = $this->schemaDN(); + $schema_dn = $this->schemaDN('default'); $schema = $this->fetch($schema_dn); switch ($item) { @@ -526,11 +526,16 @@ final class Server foreach ($this->objectclasses as $o) foreach ($o->getSupClasses() as $parent) { $parent = strtolower($parent); - if ($this->objectclasses->has($parent) !== FALSE) + + if (! $this->objectclasses->contains($parent)) $this->objectclasses[$parent]->addChildObjectClass($o->name); } return $this->objectclasses; + + // Shouldnt get here + default: + throw new InvalidUsage('Invalid request to fetch schema: '.$item); } }); diff --git a/app/Http/Middleware/ApplicationSession.php b/app/Http/Middleware/ApplicationSession.php index 20fdde7..a5472b0 100644 --- a/app/Http/Middleware/ApplicationSession.php +++ b/app/Http/Middleware/ApplicationSession.php @@ -2,9 +2,12 @@ namespace App\Http\Middleware; +use Closure; +use Config; +use Illuminate\Http\Request; + use App\Classes\LDAP\Server; use App\Ldap\User; -use Closure; /** * This sets up our application session with any required values, ultimately for cache optimisation reasons @@ -14,13 +17,13 @@ class ApplicationSession /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param Request $request + * @param Closure $next * @return mixed */ - public function handle($request,Closure $next) + public function handle(Request $request,Closure $next): mixed { - \Config::set('server',new Server); + Config::set('server',new Server); view()->share('user', auth()->user() ?: new User); diff --git a/app/Http/Middleware/CheckUpdate.php b/app/Http/Middleware/CheckUpdate.php index 725d599..6e20da8 100644 --- a/app/Http/Middleware/CheckUpdate.php +++ b/app/Http/Middleware/CheckUpdate.php @@ -3,7 +3,9 @@ namespace App\Http\Middleware; use Closure; +use Config; use GuzzleHttp\Client; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; @@ -15,13 +17,13 @@ class CheckUpdate /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next + * @param Request $request + * @param Closure $next * @return mixed */ - public function handle($request, Closure $next) + public function handle(Request $request, Closure $next): mixed { - \Config::set('update_available',Cache::get('upstream_version')); + Config::set('update_available',Cache::get('upstream_version')); return $next($request); } @@ -31,7 +33,7 @@ class CheckUpdate * * @return void */ - public function terminate() + public function terminate(): void { Cache::remember('upstream_version',self::UPDATE_TIME,function() { // CURL call to URL to see if there is a new version @@ -40,7 +42,6 @@ class CheckUpdate $client = new Client; try { - $response = $client->request('POST',sprintf('%s/%s',self::UPDATE_SERVER,strtolower(config('app.version')))); if ($response->getStatusCode() === 200) { diff --git a/app/Http/Middleware/SwapinAuthUser.php b/app/Http/Middleware/SwapinAuthUser.php index 2c7656f..c57a615 100644 --- a/app/Http/Middleware/SwapinAuthUser.php +++ b/app/Http/Middleware/SwapinAuthUser.php @@ -6,24 +6,23 @@ use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Cookie; -// use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\Log; -// use Illuminate\Support\Facades\Session; use LdapRecord\Container; use App\Ldap\Connection; class SwapinAuthUser { - /** - * Handle an incoming request. - * - * @param \Illuminate\Http\Request $request - * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next - * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse - */ - public function handle(Request $request, Closure $next) - { + /** + * Handle an incoming request. + * + * @param \Illuminate\Http\Request $request + * @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next + * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse + * @throws \LdapRecord\Configuration\ConfigurationException + */ + public function handle(Request $request,Closure $next): mixed + { $key = config('ldap.default'); /* @@ -35,16 +34,17 @@ class SwapinAuthUser } else */ + // @todo it seems sometimes we have cookies that show the logged in user, but Auth::user() has expired? if (Cookie::has('username_encrypt') && Cookie::has('password_encrypt')) { Config::set('ldap.connections.'.$key.'.username',Cookie::get('username_encrypt')); Config::set('ldap.connections.'.$key.'.password',Cookie::get('password_encrypt')); Log::debug('Swapping out configured LDAP credentials with the user\'s cookie.',['key'=>$key,'user'=>Cookie::get('username_encrypt')]); + + // We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them. + Container::getInstance()->addConnection(new Connection(config('ldap.connections.'.$key)),$key); } - // We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them. - Container::getInstance()->addConnection(new Connection(config('ldap.connections.'.$key)),$key); - return $next($request); - } + } } \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index 1f21cea..9115ee6 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -16,8 +16,8 @@ return Application::configure(basePath: dirname(__DIR__)) ) ->withMiddleware(function (Middleware $middleware) { $middleware->appendToGroup('web', [ - ApplicationSession::class, SwapinAuthUser::class, + ApplicationSession::class, CheckUpdate::class, ]); diff --git a/docker/Dockerfile b/docker/Dockerfile index 1a877d1..7611cdd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -9,7 +9,7 @@ RUN install-php-extensions \ memcached RUN sed -i -e 's/^{$CADDY_EXTRA_CONFIG}$/{$CADDY_EXTRA_CONFIG} /' /etc/caddy/Caddyfile -RUN sed -i -e 's/^memory_limit = 128M/memory_limit = 1G/' /usr/local/etc/php/php.ini-production +RUN sed -i -e 's/^memory_limit = 128M/memory_limit = 256M/' /usr/local/etc/php/php.ini-production RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini RUN curl -4 https://getcomposer.org/installer|php -- --install-dir=/usr/local/bin --filename=composer