Althinect / filament-spatie-roles-permissions

MIT License
256 stars 87 forks source link

role_has_permissions table not populating - can't use permissions via role #181

Closed christracey closed 5 months ago

christracey commented 5 months ago

Hi,

I took over a Laravel 10 project in its early stages from another developer. The Spatie laravel-permission package was already installed but hadn't been implemented. I installed the althinect/filament-spatie-roles-permissions package for Filament. As I'm trying to implement permission checks based on best practices I'm running into an issue - when assigning permissions to a role via the filament resources pages, the role_has_permissions table does not get populated but model_has_permissions table does. So when I try to use $user->can() it returns false even though the permission is attached to the role in model_has_permissions.

If I manually add the relationship to role_has_permissions and reset the cache then $user->can() works as expected. Additionally, if I manually add permissions to a role with $role->givePermissionTo('permission name') or $role->syncPermissions(['permission name']) then the relationship is added to role_has_permissions and can() works.

I started a new project from scratch and installed the package and all works as expected without any special setup. The only thing I can think of is that the previous dev made some configuration that is breaking the plugin.

So I guess the main question is - what would cause the role_has_permissions table to not be populated when attaching permissions to roles via the filament resources pages provided by this plugin?

drbyte commented 5 months ago

role_has_permissions is only populated when $role->givePermissionTo() is called, or a permission is linked to a role by any of the other ways that the Spatie package provides for linking permissions to roles. Calling $user->assignRole() will only link a User to a Role. Then it's up to whatever that Role has been given permission to do. So your observations are correct. I don't see anything out of the ordinary in what you describe.

christracey commented 5 months ago

Thanks for responding - I misspoke in my original post so I edited to correct. Calling the spatie methods directly works fine. It's when I use the resource pages included in the filament-spatie-roles-permissions package that the role_has_permissions table does not populate - only model_has_permissions.

drbyte commented 5 months ago

Since you said installing it all fresh onto a new Laravel project works fine, I'd be inclined to look for all the files where parts from this package are referenced, and re-think their use. Or, make a backup, rip out everything related to this and the core spatie package, deleting the related permissions tables (un-migrate that one migration file, and also the teams migration if used, or at least look at the migrations' "down" methods to get all the deletion steps done). Make sure your app works fine w/o the permissions stuff (no major errors thrown, confirming you didn't break something unexpected by ripping things out). And then re-implement from scratch. The plus of this approach is that you will be more intimately familiar with what's done to integrate it to your app, because you did it yourself. And you'll be better able to diagnose issues as you go along. Your backup of the prior implementation will be a useful reference for sorting out the many things that had been integrated prior. Sorry, that's not a super simple solution, nor short ... but it's probably the wisest.

christracey commented 5 months ago

I agree. I actually took that approach yesterday. I created a new branch on local, removed spatie permissions table and config file, removed the Althinect package, removed all policies, then reinstalled and recreated everything. I've been at this for several days now with no luck. I even removed the use of uuids because I thought that may have been related. I'll probably give rebuilding another shot like you suggested in case I missed something.

I also dug into the Althinect package and I can't see where it is calling any of the Spatie methods to create roles or permissions or attach permissions to roles. Do you happen to know if that is being done somewhere else? I suppose filament could be handling that?

tharindarodrigo commented 5 months ago

@christracey Creation of Roles and Permissions is handled by Filament itself.

Is the package updated to the latest version on your original application?

If it helps. there was a problem when attaching Permissions to a role. The permissions wouldn't sync properly when the Spatie methods were used as we wanted to attach many permissions to many roles. Therefore we had to fallback to Laravel methods.

The scenario would look like

christracey commented 5 months ago

Thanks for that info - I just updated both filament/filament and althinect/filament-spatie-roles-permissions to the latest versions and now I'm getting a 404 when navigating to a role or permission view or edit page. The list pages work fine. I verified the routes exist with php artisan view:list. View and edit pages for all other resources work fine. I'm going to troubleshoot this new issue for a while longer then roll back if I can't get it fixed.

Edit: User error. Somehow my extended models for role and permission reverted back to using uuid's. I removed the code for uuid's in the models and the 404 is gone. Initial testing is still yielding the same result - attaching permissions to roles via the edit or create role resource pages does not populate any data in role_has_permissions.

christracey commented 5 months ago

Update:

Ok I have confirmed that if I do as you suggested (use 'attach to roles' bulk action button on permissions page) then it does populate role_has_permissions! This is great progress. I also confirmed that if I go to the edit page of an individual permission and attach a role then the role_has_permissions table gets populated.

So what does this mean? Attaching permissions to a role via the role create/edit page simply cannot and should not be used?

Thanks!

christracey commented 5 months ago

@tharindarodrigo so the workaround is working fine to bulk attach permissions to a role on the permissions list page. The problem now is that when I go to the page to view or edit a role it does not list any of the permissions that have been added to role_has_permissions. I guess that page only pulls from model_has_permissions?

Since it all worked fine on the new app I built I assume it's possible. Any other thoughts on what I could look at?

christracey commented 5 months ago

I don't know if this was related but I realized that prior to moving to filament the app was using nova with this package installed: https://packagist.org/packages/sereny/nova-permissions

I noticed this because in one of the older migrations to create roles and permissions there was a 'group' column being created in the permissions table. I couldn't find this column associated with the spatie package so I started digging. No clue if this is related to my issues or not but I thought I'd throw it out there in case anything jumped out. I'm still investigating...

tharindarodrigo commented 5 months ago

I do not recall a "group" column within the package. Have you enabled teams within Spatie Permissions?

christracey commented 5 months ago

I did not enable teams, no. The group looks to come from a migration created by the nova permissions package that is also built off of the spatie package.

christracey commented 5 months ago

Another update - I don't know if I was mistaken in my original post or if something changed but now when I call $role->assignPermissionTo('permission_name'); it only creates an entry in model_has_permissions...not role_has_permissions

I had thought using the spatie methods directly worked fine but i was wrong apparently

christracey commented 5 months ago

Ok I got it working but I'm not entirely sure why. If anyone can explain the reasoning I'd be grateful. Here is what I found...

In the spatie config located in config/permission.php both the models.permission and models.role had been modified.

    //'permission' => Spatie\Permission\Models\Permission::class,
    'permission' => App\Models\Permission::class,

and

    //'role' => Spatie\Permission\Models\Role::class,
    'role' => App\Models\Role::class,

If I switch back to using the Spatie\Permission\Models\Permission::class and Role::class then everything works. The model_has_roles stops getting populated and the role_has_permissions starts getting populated so I can use the standard permission checks like ->can(). Success!!

Now I'm not sure why it doesn't work with the extended models. They're basic and look to follow the recommendations by the spatie docs.

App\Models\Permission.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Models\Permission as SpatiePermission;
use Spatie\Permission\Traits\RefreshesPermissionCache;

class Permission extends SpatiePermission
{
    use RefreshesPermissionCache;
}

App\Models\Role.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\Permission\Models\Role as SpatieRole;
use Spatie\Permission\Traits\HasPermissions;
use Spatie\Permission\Traits\RefreshesPermissionCache;

class Role extends SpatieRole
{
    use HasPermissions, RefreshesPermissionCache;
}

So I'm happy that I got it working but I'm not quite sure why simply extending the models broke it. I'd love to learn if anyone cares to share.

Thanks for all your help!

tharindarodrigo commented 5 months ago

I guess if you use the HasPermission trait in Role, it will populate the model_has_permissions table. Try removing it while you extend the SpatieRole. The Role class is set to populate the role_has_permissions table by default. Typically you use the HasPermisison trait within the User Model.

christracey commented 5 months ago

You're right! Removing the HasPermissions trait and it works as intended with extending the models. Funny though I could have sworn I compared the models side-by-side in the new project I created to test and it still worked.

oh well. it works now! Thank you!