DirectoryTree / LdapRecord-Laravel

Multi-domain LDAP Authentication & Management for Laravel.
https://ldaprecord.com/docs/laravel/v3
MIT License
483 stars 51 forks source link

[Bug] Problems on Laravel Nova 4 in combination with Spatie Permissions package #644

Closed werner-h closed 3 months ago

werner-h commented 3 months ago

Environment:

Describe the bug:

I have been experiencing problems with Spatie Permissions v6 and Laravel Nova v4 in connection with the LdapRecord User Model LdapRecord\Models\ActiveDirectory\User::class for the last few days

As soon as the Actionable trait is used, for example, I get the error message:

[2024-03-27 09:31:22] local.ERROR: The LDAP connection [mysql] does not exist. {"userId":1,"exception":"[object] (LdapRecord\\ContainerException(code: 0): The LDAP connection [mysql] does not exist. at C:\\laragon\\www\\example-site\\vendor\\directorytree\\ldaprecord\\src\\ConnectionManager.php:92)
[stacktrace]
#0 C:\\laragon\\www\\example-site\\vendor\\directorytree\\ldaprecord\\src\\Container.php(63): LdapRecord\\ConnectionManager->getConnection('mysql')
#1 C:\\laragon\\www\\example-site\\vendor\\directorytree\\ldaprecord\\src\\Models\\Model.php(330): LdapRecord\\Container->__call('getConnection', Array)
#2 C:\\laragon\\www\\example-site\\vendor\\directorytree\\ldaprecord\\src\\Models\\Model.php(304): LdapRecord\\Models\\Model::resolveConnection('mysql')
#3 C:\\laragon\\www\\example-site\\vendor\\directorytree\\ldaprecord\\src\\Models\\Model.php(295): LdapRecord\\Models\\Model->newQueryWithoutScopes()
#4 C:\\laragon\\www\\example-site\\vendor\\laravel\\framework\\src\\Illuminate\\Database\\Eloquent\\Concerns\\HasRelationships.php(606): LdapRecord\\Models\\Model->newQuery()

Spatie Permissions with Sereny Nova Permissions package:

[2024-03-27 09:57:13] local.ERROR: Class name must be a valid object or a string {"userId":1,"exception":"[object] (Error(code: 0): Class name must be a valid object or a string at C:\\laragon\\www\\example-site\\vendor\\sereny\\nova-permissions\\src\\Nova\\Permission.php:101)
[stacktrace]
#0 C:\\laragon\\www\\example-site\\vendor\\laravel\\nova\\src\\ResolvesFields.php(626): Sereny\\NovaPermissions\\Nova\\Permission->fields(Object(Laravel\\Nova\\Http\\Requests\\NovaRequest))
#1 C:\\laragon\\www\\example-site\\vendor\\laravel\\nova\\src\\ResolvesFields.php(615): Laravel\\Nova\\Resource->buildAvailableFields(Object(Laravel\\Nova\\Http\\Requests\\NovaRequest), Array)
#2 C:\\laragon\\www\\example-site\\vendor\\laravel\\nova\\src\\ResolvesFields.php(201): Laravel\\Nova\\Resource->availableFieldsOnIndexOrDetail(Object(Laravel\\Nova\\Http\\Requests\\NovaRequest))
#3 C:\\laragon\\www\\example-site\\vendor\\laravel\\nova\\src\\ResolvesFilters.php(43): Laravel\\Nova\\Resource->filterableFields(Object(Laravel\\Nova\\Http\\Requests\\NovaRequest))
#4 C:\\laragon\\www\\example-site\\vendor\\laravel\\nova\\src\\ResolvesFilters.php(18): Laravel\\Nova\\Resource->resolveFiltersFromFields(Object(Laravel\\Nova\\Http\\Requests\\NovaRequest))

Here the lines from line 101:

$userResource = $this->userResource();

MorphToMany::make($userResource::label(), 'users', $userResource)
                ->searchable()
                ->canSee(function ($request) {
                    return $this->fieldAvailable('users');
                }),

The lines from Resource.php

    protected function userResource()
    {
        $model = $this->modelForGuard();

        return Nova::resourceForModel($model);
    }

The trait for the resolution:

trait ModelForGuardResolver {

    /**
     * Determines the guard model class
     *
     * @return class-string
     */
    public function modelForGuard()
    {
        return ModelForGuardState::$resolveModelForGuardCallback
            ? call_user_func(ModelForGuardState::$resolveModelForGuardCallback)
            : getModelForGuard($this->guard_name);
    }
}

The helper function from spatie for the resolution:

if (! function_exists('getModelForGuard')) {
    /**
     * @return string|null
     */
    function getModelForGuard(string $guard)
    {
        return collect(config('auth.guards'))
            ->map(fn ($guard) => isset($guard['provider']) ? config("auth.providers.{$guard['provider']}.model") : null)
            ->get($guard);
    }
}

With regard to the Nova Permissions Package, I can help myself with my own role and permissions model so that I can skip the functions shown above.

But somehow it keeps catching me up with the message "The LDAP connection [mysql] does not exist.".

If I temporarily revert to the Eloquent model, I do not receive any error messages. How can I pass on the Eloquent relevant methods with a custom LDAP user model so that they are resolved correctly?

stevebauman commented 3 months ago

Hey @werner-h,

Spatie has known incompatibility with this package

Please see https://github.com/DirectoryTree/LdapRecord-Laravel/issues/150#issuecomment-632943856

werner-h commented 3 months ago

Why is there no interest in solving the problem? It does not only seem to lead to problems with Spatie, the Actionable Trait also does not work on Laravel Nova, as it always leads to the error "The LDAP connection [mysql] does not exist".

werner-h commented 3 months ago

Update: It seems that I have found a solution at least for Spatie Soft by using the function from https://github.com/serenysoft/nova-permissions:

->resolveModelForGuardUsing(function() {
    $guard = config('nova.guard') ?: config('auth.defaults.guard');

    $provider = config("auth.guards.{$guard}.provider");

    return config("auth.providers.{$provider}.database.model");
})

I also need to use custom Role and Permission Nova Model to overwrite the User Model resolution. Would be awesome if Nova is gonna have a look onto your request: #https://github.com/laravel/nova-issues/discussions/4082

stevebauman commented 3 months ago

Why is there no interest in solving the problem?

This isn't the case. What you're encountering here are incompatibilities between packages -- not a uni-directional incompatibility with LdapRecord-Laravel itself. And with all due respect, I'm not responsible for how Spatie Permission works or integrates with other packages. I also don't use Spatie Permission, but if you would like to submit a PR that provides that compatibility in a non-BC way, then I'd gladly take a look at getting it merged.

the Actionable Trait also does not work on Laravel Nova, as it always leads to the error "The LDAP connection [mysql] does not exist".

The Actionable trait cannot be applied to LdapRecord models. It contains an Eloquent relationship method designed only to be used with Eloquent. LdapRecord and Eloquent models are designed to interact with two entirely different technology backends (SQL vs LDAP). These models cannot be used interchangeably.