The Problem
Yesterday I was working on a ACL Implementation and I was using the Spatie/Permissions library all was ok, until I had to run a clean migration to test all my factories.
I was getting the following error:
Spatie\Permission\Exceptions\PermissionAlreadyExists : A `view backend` permission already exists for guard `web`.
I was confused the Permissions table was empty, I was forcing to clear the permission cache file just before starting to add new permissions.
// Reset cached roles and permissions
app()['cache']->forget('spatie.permission.cache');
I even ran my custom composer clear-all
command to clear all the cache from views/routes/permission etc.
> @php artisan clear-compiled
Compiled services and packages files removed!
> @php artisan cache:clear
Application cache cleared!
> @php artisan route:clear
Route cache cleared!
> @php artisan view:clear
Compiled views cleared!
> @php artisan config:clear
Configuration cache cleared!
> @php artisan permission:cache-reset
Permission cache flushed.
> composer dumpautoload -o
Generating optimized autoload files> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan ide-helper:generate
A new helper file was written to _ide_helper.php
> @php artisan ide-helper:meta
A new meta file was written to .phpstorm.meta.php
> @php artisan package:discover --ansi
Discovered Package: arcanedev/log-viewer
Discovered Package: arcanedev/no-captcha
Discovered Package: barryvdh/laravel-debugbar
Discovered Package: barryvdh/laravel-ide-helper
Discovered Package: beyondcode/laravel-dump-server
Discovered Package: creativeorange/gravatar
Discovered Package: davejamesmiller/laravel-breadcrumbs
Discovered Package: fideloper/proxy
Discovered Package: hieu-le/active
Discovered Package: ivaano/laravel-code-generator
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Discovered Package: spatie/laravel-html
Discovered Package: spatie/laravel-permission
Discovered Package: webpatser/laravel-uuid
Package manifest generated successfully.
Generated optimized autoload files containing 4595 classes
But the error kept showing every time i tried to seed, It worked if I changed the permission name or created a new permissions, but it didnt worked with the view backend
permission. I spent about an hour looking into this, this happened on my linux laptop I didnt have this issue on OSX, so I left that until Today, so Today I was looking at the permissions library and didnt found anything weird, that was until I’ve decided to see what permissions where being returned, this is the method from the Permissions library
/**
* Get the permissions based on the passed params.
*
* @param array $params
*
* @return \Illuminate\Support\Collection
*/
public function getPermissions(array $params = []): Collection
{
if ($this->permissions === null) {
$this->permissions = $this->cache->remember(self::$cacheKey, self::$cacheExpirationTime, function () {
return $this->getPermissionClass()
->with('roles')
->get();
});
}
$permissions = clone $this->permissions;
foreach ($params as $attr => $value) {
$permissions = $permissions->where($attr, $value);
}
return $permissions;
}
So I found that the cache was returning the view backend
permission even though I have cleared the cache, so I dig a little more and found the file that was being called by the cacheManager class, and it was there and it did in fact had the view backedn
key, so the command php artisan permission:cache-reset
was not clearing the cache also calling it from the code was failing app()['cache']->forget('spatie.permission.cache');
. (It was expected as the command is just a wrapper for this call)
In the end the problem was the file ownership, the cache was created by the apache user www-data
, on OSX I didnt have this issue because I’m running apache with the same user I’m logged in.
The Solution
A quick solution would be to just remove the file manually, but I will have to find the file and remove it manually everytime I need to clear the cache, so the next approach would be to make apache run as the user I’m logged just like my OSX approach, this would also have other benefits in my dev machine, as I wont have to worry about files created by apache that I cant manage from the cli with my user.
Running apache as a different user is pretty simple in ubuntu just change the user:group in the file /etc/apache2/envvars
, restart apache and change all the files to the user that will be running apache.
Lessons learned
Silent Failing is always bad, If I dont see an error where I’m expecting certain behavior I will assume the problem is somewhere else, so it will be a waste of time trying to find the error. This is a Library problem, laravel is returning false when the delete method is called /laravel/framework/src/Illuminate/Filesystem/Filesystem.php
, but the library is just assuming that there will be no problems when the cache is cleared.
The fix is simple, just return the result from the cache call and if it fails at least print an error, I’ve sent a PR Show error when cache:reset command fails to fix this, hopefully it gets accepted.