I'm having an issue with Laravel's file/folder permissions when they are generated from different sources. This may be a bit long winded, but bear with me.
Scenario
1) An artisan
command, called TestCommand.php
, triggered via php artisan test:command
has the following code:
public function handle(){
\Log::info("Test Command Executed from Command Line");
}
2) A Contoller Function in TestController.php
, triggered when accessing http://localhost/test
, which has the following code:
public function test(){
\Log::info("Test Command Executed from Web View");
}
Both functions work correctly when triggered individually, but as soon as one is triggered, the other will not work, as the generated log file in storage/logs
now has incorrect permissions, user and/or group.
Situation 1
If I run php artisan test:command
, I get the following log file:
-rw-r--r-- | 1 | tim | wheel | 76B | 25 Aug 14:03 | laravel-2017-08-25.log
And navigating to http://localhost/test
returns the following error:
This page isn’t working
localhost is currently unable to handle this request.
HTTP ERROR 500
No Laravel stack trace ("Woops, something went wrong") is displayed, as that only happens following writing to the Laravel logs.
Situation 2
If I navigate to http://localhost/test
first, I get this log file:
-rw-r--r-- | 1 | _www | wheel | 72B | 25 Aug 14:06 | laravel-2017-08-25.log
And running the php artisan test:command
returns the following:
PHP Fatal error: Uncaught exception 'UnexpectedValueException' with message 'The stream or file ".../storage/logs/laravel-2017-08-25.log" could not be opened: failed to open stream: Permission denied' in .../vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php:97 Stack trace: ...
Conclusion
Notice the user is now _www
instead of tim
, which is to be expected given the generation source of the file in storage/logs
. Retroactively, this can be fixed quite easily by running:
sudo chmod -R 777 storage/logs
But that's not a good solution for a number of reasons:
storage/framework/*
, etc) this command would need to be run again.Given all this, how do I unify the files to allow either source to create the file and then modify the file as needed?
Webserver is apache, dev environment is Mac, production is linux
Update
I think a good solution is to have tim
and _www
in the same group, and assign storage/logs
to that group using
chgrp -R <user_group> storage
But then the next issue is that by default, the permissions on any generated folder in Laravel is 644
, which doesn't allow write access by the group
, only read.
Will look into modifying this if possible.
Found the solution to this issue; ACLs!
On Mac, there's an option for chmod +a ...
, which will add default directory permissions to whatever folder you specify. In my case, I want to use ACLs that allow tim
and _www
full access to files/folders under storage
, since they'll both be accessing/writing to it constantly:
chmod -R +a 'tim allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit' storage
chmod -R +a '_www allow read,write,delete,add_file,add_subdirectory,file_inherit,directory_inherit' storage
Applying this recursively allows ACLs to be set on any folders generated by Laravel in the storage directory, most important being those is storage/framework/cache
.
On Linux, ACLs seem to be slightly different, but the idea is the same:
chmod g+s storage
setfacl -R -d -m u::rwx storage
setfacl -R -d -m g::rx storage
setfacl -R -d -m o::rx storage
This would effectively set the file permissions to storage (and all subfolders) to 755
, which seems to be what my configuration needs to allow writing properly.