├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── spec └── Umpirsky │ └── PermissionsHandler │ ├── ConfigurationSpec.php │ └── SetfaclPermissionsSetterSpec.php └── src └── PermissionsHandler ├── ChmodPermissionsSetter.php ├── Configuration.php ├── Exception ├── ExceptionInterface.php ├── InvalidConfigurationException.php └── PathNotFoundException.php ├── PermissionsSetter.php ├── PermissionsSetterInterface.php ├── ScriptHandler.php └── SetfaclPermissionsSetter.php /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | vendor 3 | composer.lock 4 | .idea 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.2 4 | 5 | install: 6 | - composer install 7 | 8 | script: php bin/phpspec run -f pretty 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Saša Stamenković 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 |

7 | symfony upgrade fixer • 8 | twig gettext extractor • 9 | wisdom • 10 | centipede • 11 | permissions handler • 12 | extraload • 13 | gravatar • 14 | locurro • 15 | country list • 16 | transliterator 17 |

18 | 19 | # Composer Permissions Handler [![Build Status](https://travis-ci.org/umpirsky/PermissionsHandler.svg?branch=master)](https://travis-ci.org/umpirsky/PermissionsHandler) 20 | 21 | Composer script handling directories permissions by making them writable both by the web server and the command line user. 22 | 23 | ## Usage 24 | 25 | Add the following in your root composer.json file: 26 | 27 | ```json 28 | { 29 | "require": { 30 | "umpirsky/composer-permissions-handler": "~1.0" 31 | }, 32 | "scripts": { 33 | "post-install-cmd": [ 34 | "Umpirsky\\PermissionsHandler\\ScriptHandler::setPermissions" 35 | ] 36 | }, 37 | "extra": { 38 | "writable-dirs": ["app/cache", "app/logs"] 39 | } 40 | } 41 | ``` 42 | 43 | `app/cache` and `app/logs` are directories we want writable by the web server and the command line user. 44 | 45 | ## Examples 46 | 47 | * [Symfony](https://github.com/umpirsky/symfony-standard/tree/feature/permissions-handler) 48 | * [Lavarel](https://github.com/umpirsky/laravel/tree/feature/permissions-handler) 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "umpirsky/composer-permissions-handler", 3 | "description": "Composer script handling directories permissions by making them writable both by the web server and the command line user.", 4 | "keywords": [ 5 | "permissions handling" 6 | ], 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Saša Stamenković", 11 | "email": "umpirsky@gmail.com" 12 | } 13 | ], 14 | "require": { 15 | "php": "^7.2 || ^8.0", 16 | "symfony/process": "^2.3 || ^3.0 || ^4.0 || ^5.0" 17 | }, 18 | "require-dev": { 19 | "composer/composer": "^2.2.4", 20 | "phpspec/phpspec": "^7.1" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "Umpirsky\\": "src", 25 | "Spec\\": "spec" 26 | } 27 | }, 28 | "config": { 29 | "bin-dir": "bin" 30 | }, 31 | "branch-alias": { 32 | "dev-master": "1.0-dev" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /spec/Umpirsky/PermissionsHandler/ConfigurationSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($event); 17 | 18 | $event->getComposer()->shouldBeCalled()->willReturn($composer); 19 | $composer->getPackage()->shouldBeCalled()->willReturn($package); 20 | $package->getExtra()->shouldBeCalled()->willReturn( 21 | ['writable-dirs' => ['app/cache', 'app/logs']] 22 | ); 23 | } 24 | 25 | function it_is_initializable() 26 | { 27 | $this->shouldHaveType('APP\PermissionsHandler\Configuration'); 28 | } 29 | 30 | function it_gets_writable_dirs() 31 | { 32 | $this->getWritableDirs()->shouldReturn(['app/cache', 'app/logs']); 33 | } 34 | 35 | function it_throws_exception_if_writable_dirs_are_not_configured($package) 36 | { 37 | $package->getExtra()->shouldBeCalled()->willReturn([]); 38 | 39 | $this->shouldThrow('APP\PermissionsHandler\Exception\InvalidConfigurationException')->duringGetWritableDirs(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /spec/Umpirsky/PermissionsHandler/SetfaclPermissionsSetterSpec.php: -------------------------------------------------------------------------------- 1 | beConstructedWith($process); 15 | } 16 | 17 | function it_is_initializable() 18 | { 19 | $this->shouldHaveType('APP\PermissionsHandler\SetfaclPermissionsSetter'); 20 | } 21 | 22 | function it_is_permissions_setter() 23 | { 24 | $this->shouldHaveType('APP\PermissionsHandler\PermissionsSetterInterface'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/PermissionsHandler/ChmodPermissionsSetter.php: -------------------------------------------------------------------------------- 1 | runCommand('chmod +a "%httpduser% allow delete,write,append,file_inherit,directory_inherit" %path%', $path); 18 | $this->runCommand('chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" %path%', $path); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/PermissionsHandler/Configuration.php: -------------------------------------------------------------------------------- 1 | configuration = $event->getComposer() 17 | ->getPackage() 18 | ->getExtra(); 19 | } 20 | 21 | /** 22 | * @return array 23 | */ 24 | public function getWritableDirs(): array 25 | { 26 | if (!isset($this->configuration['writable-dirs'])) { 27 | throw new InvalidConfigurationException('The writable-dirs must be specified in composer arbitrary extra data.'); 28 | } 29 | 30 | if (!is_array($this->configuration['writable-dirs'])) { 31 | throw new InvalidConfigurationException('The writable-dirs must be an array.'); 32 | } 33 | 34 | return $this->configuration['writable-dirs']; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PermissionsHandler/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | process = $process; 17 | } 18 | 19 | /** 20 | * @return string 21 | */ 22 | protected function getHttpdUser(): string 23 | { 24 | return $this->runProcess( 25 | "ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1" 26 | ); 27 | } 28 | 29 | /** 30 | * @param $command 31 | * @param $path 32 | * @return void 33 | */ 34 | protected function runCommand($command, $path) 35 | { 36 | $this->runProcess(str_replace( 37 | ['%httpduser%', '%path%'], 38 | [$this->getHttpdUser(), $path], 39 | $command 40 | )); 41 | } 42 | 43 | /** 44 | * @param $commandline 45 | * @return string 46 | */ 47 | protected function runProcess($commandline): string 48 | { 49 | if (null === $this->process) { 50 | $this->process = new Process(null); 51 | } 52 | 53 | $this->process->setCommandLine($commandline); 54 | $this->process->run(); 55 | if (!$this->process->isSuccessful()) { 56 | throw new ProcessFailedException($this->process); 57 | } 58 | 59 | return trim($this->process->getOutput()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/PermissionsHandler/PermissionsSetterInterface.php: -------------------------------------------------------------------------------- 1 | getIO()->write('No permissions setup is required on Windows.'); 20 | 21 | return; 22 | } 23 | 24 | $event->getIO()->write('Setting up permissions.'); 25 | 26 | try { 27 | self::setPermissionsSetfacl($event); 28 | 29 | return; 30 | } catch (ProcessFailedException $setfaclException) { 31 | $event->getIO()->write(sprintf('%s', $setfaclException->getMessage())); 32 | $event->getIO()->write('Trying chmod...'); 33 | } 34 | 35 | try { 36 | self::setPermissionsChmod($event); 37 | 38 | return; 39 | } catch (ProcessFailedException $chmodException) { 40 | $event->getIO()->write(sprintf('%s', $chmodException->getMessage())); 41 | } 42 | } 43 | 44 | /** 45 | * @param Event $event 46 | * @return void 47 | */ 48 | public static function setPermissionsSetfacl(Event $event) 49 | { 50 | self::setPermissionsWithSetter($event, new SetfaclPermissionsSetter()); 51 | } 52 | 53 | /** 54 | * @param Event $event 55 | * @return void 56 | */ 57 | public static function setPermissionsChmod(Event $event) 58 | { 59 | self::setPermissionsWithSetter($event, new ChmodPermissionsSetter()); 60 | } 61 | 62 | /** 63 | * @param Event $event 64 | * @param PermissionsSetterInterface $permissionsSetter 65 | * @return void 66 | */ 67 | private static function setPermissionsWithSetter(Event $event, PermissionsSetterInterface $permissionsSetter) 68 | { 69 | foreach ((new Configuration($event))->getWritableDirs() as $path) { 70 | $permissionsSetter->setPermissions($path); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/PermissionsHandler/SetfaclPermissionsSetter.php: -------------------------------------------------------------------------------- 1 | runCommand('setfacl -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path); 22 | $this->runCommand('setfacl -d -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path); 23 | } 24 | } 25 | --------------------------------------------------------------------------------