From ce5189b2388da7ee7555c1cd15150789889366ed Mon Sep 17 00:00:00 2001 From: Gautier DELEGLISE Date: Wed, 9 Apr 2025 22:46:57 +0200 Subject: [PATCH 1/7] =?UTF-8?q?=E2=9C=A8=20commands?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/AccessServiceProvider.php | 55 +++++++ src/Console/ControlMakeCommand.php | 193 +++++++++++++++++++++++ src/Console/PerimeterMakeCommand.php | 126 +++++++++++++++ src/Console/stubs/control.stub | 21 +++ src/Console/stubs/perimeter.overlay.stub | 11 ++ src/Console/stubs/perimeter.plain.stub | 11 ++ src/Policies/ControlledPolicy.php | 28 +++- tests/Feature/ControlsQueryTest.php | 2 + tests/Feature/ControlsShouldTest.php | 2 + tests/Unit/Console/MakeCommandsTest.php | 98 ++++++++++++ tests/Unit/StubsTest.php | 22 +++ tests/Unit/TestCase.php | 2 + 12 files changed, 570 insertions(+), 1 deletion(-) create mode 100644 src/Console/ControlMakeCommand.php create mode 100644 src/Console/PerimeterMakeCommand.php create mode 100644 src/Console/stubs/control.stub create mode 100644 src/Console/stubs/perimeter.overlay.stub create mode 100644 src/Console/stubs/perimeter.plain.stub create mode 100644 tests/Unit/Console/MakeCommandsTest.php create mode 100644 tests/Unit/StubsTest.php diff --git a/src/AccessServiceProvider.php b/src/AccessServiceProvider.php index f9b8d4e..daa1aab 100644 --- a/src/AccessServiceProvider.php +++ b/src/AccessServiceProvider.php @@ -2,10 +2,19 @@ namespace Lomkit\Access; +use Illuminate\Foundation\Events\PublishingStubs; +use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider; +use Lomkit\Access\Console\ControlMakeCommand; +use Lomkit\Access\Console\PerimeterMakeCommand; class AccessServiceProvider extends ServiceProvider { + protected array $devCommands = [ + 'ControlMake' => ControlMakeCommand::class, + 'PerimeterMake' => PerimeterMakeCommand::class, + ]; + /** * Registers the service provider. * @@ -13,6 +22,8 @@ class AccessServiceProvider extends ServiceProvider */ public function register() { + $this->registerCommands($this->devCommands); + $this->mergeConfigFrom( __DIR__.'/../config/access-control.php', 'access-control' @@ -27,6 +38,40 @@ public function register() public function boot() { $this->registerPublishing(); + + $this->registerStubs(); + } + + /** + * Register the given commands. + * + * @param array $commands + * @return void + */ + protected function registerCommands(array $commands) + { + foreach ($commands as $commandName => $command) { + $method = "register{$commandName}Command"; + + if (method_exists($this, $method)) { + $this->{$method}(); + } else { + $this->app->singleton($command); + } + } + + $this->commands(array_values($commands)); + } + + /** + * Register the stubs on the default laravel stub publish command + */ + protected function registerStubs() { + Event::listen(function (PublishingStubs $event) { + $event->add(realpath(__DIR__.'/Console/stubs/control.stub'), 'controller.stub'); + $event->add(realpath(__DIR__.'/Console/stubs/perimeter.plain.stub'), 'perimeter.plain.stub'); + $event->add(realpath(__DIR__.'/Console/stubs/perimeter.overlay.stub'), 'perimeter.overlay.stub'); + }); } /** @@ -42,4 +87,14 @@ private function registerPublishing() ], 'access-control-config'); } } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array_values($this->devCommands); + } } diff --git a/src/Console/ControlMakeCommand.php b/src/Console/ControlMakeCommand.php new file mode 100644 index 0000000..94fd938 --- /dev/null +++ b/src/Console/ControlMakeCommand.php @@ -0,0 +1,193 @@ +resolveStubPath('/stubs/control.stub'); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + /** + * Get the default namespace for the class. + * + * @param string $rootNamespace + * @return string + */ + protected function getDefaultNamespace($rootNamespace) + { + return $rootNamespace.'\Access\Controls'; + } + + /** + * Build the class with the given name. + * + * Remove the base controller import if we are already in the base namespace. + * + * @param string $name + * @return string + */ + protected function buildClass($name) + { + $rootNamespace = $this->rootNamespace(); + $controlNamespace = $this->getNamespace($name); + + $replace = []; + + $baseControlExists = file_exists($this->getPath("{$rootNamespace}Access\Controls\Control")); + + $replace = $this->buildPerimetersReplacements($replace, $this->option('perimeters')); + + if ($baseControlExists) { + $replace["use {$controlNamespace}\Control;\n"] = ''; + } else { + $replace[' extends Control'] = ''; + $replace["use {$rootNamespace}Access\Controls\Control;\n"] = ''; + } + + return str_replace( + array_keys($replace), array_values($replace), parent::buildClass($name) + ); + } + + /** + * Build the model replacement values. + * + * @param array $replace + * @param array $perimeters + * @return array + */ + protected function buildPerimetersReplacements(array $replace, array $perimeters) + { + $perimetersImplementation = ''; + + foreach ($perimeters as $perimeter) { + $perimeterClass = app_path('Access/Perimeters/'.$perimeter.'.php'); + + $perimetersImplementation .= <<should(function (Model \$user, string \$method, Model \$model) { + return true; + }) + ->allowed(function (Model \$user) { + return true; + }) + ->query(function (Builder \$query, Model \$user) { + return \$query; + }),\\n + PERIMETER; + } + + return array_merge($replace, [ + '{{ perimeters }}' => $perimetersImplementation, + '{{perimeters}}' => $perimetersImplementation, + ]); + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + ['perimeters', 'p', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'The perimeters that the control relies on'], + ]; + } + + /** + * Interact further with the user if they were prompted for missing arguments. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return void + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + { + if ($this->didReceiveOptions($input)) { + return; + } + $perimeters = multiselect( + 'What perimeters should this control apply to? (Optional)', + $this->possiblePerimeters(), + ); + + if ($perimeters) { + $input->setOption('perimeters', $perimeters); + } + } + + /** + * Get a list of possible model names. + * + * @return array + */ + protected function possiblePerimeters() + { + $perimetersPath = is_dir(app_path('Access/Perimeters')) ? app_path('Access/Perimeters') : app_path(); + + return (new Collection(Finder::create()->files()->depth(0)->in($perimetersPath))) + ->map(fn ($file) => $file->getBasename('.php')) + ->sort() + ->values() + ->all(); + } +} diff --git a/src/Console/PerimeterMakeCommand.php b/src/Console/PerimeterMakeCommand.php new file mode 100644 index 0000000..6a6e906 --- /dev/null +++ b/src/Console/PerimeterMakeCommand.php @@ -0,0 +1,126 @@ +option('overlay')) { + $stub = "/stubs/perimeter.overlay.stub"; + } + + $stub ??= '/stubs/perimeter.plain.stub'; + + return $this->resolveStubPath($stub); + } + + /** + * Resolve the fully-qualified path to the stub. + * + * @param string $stub + * @return string + */ + protected function resolveStubPath($stub) + { + return file_exists($customPath = $this->laravel->basePath(trim($stub, '/'))) + ? $customPath + : __DIR__.$stub; + } + + /** + * Get the default namespace for the class. + * + * @param string $rootNamespace + * @return string + */ + protected function getDefaultNamespace($rootNamespace) + { + return $rootNamespace.'\Access\Perimeters'; + } + + /** + * Build the class with the given name. + * + * Remove the base controller import if we are already in the base namespace. + * + * @param string $name + * @return string + */ + protected function buildClass($name) + { + $replace = []; + + return str_replace( + array_keys($replace), array_values($replace), parent::buildClass($name) + ); + } + + /** + * Get the console command options. + * + * @return array + */ + protected function getOptions() + { + return [ + ['overlay', 'o', InputOption::VALUE_OPTIONAL, 'Indicates if the perimeter overlays'], + ]; + } + + /** + * Interact further with the user if they were prompted for missing arguments. + * + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return void + */ + protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) + { + // + } +} diff --git a/src/Console/stubs/control.stub b/src/Console/stubs/control.stub new file mode 100644 index 0000000..55424b6 --- /dev/null +++ b/src/Console/stubs/control.stub @@ -0,0 +1,21 @@ + An array of Perimeter objects. + */ + protected function perimeters(): array + { + return [{{ perimeters }}]; + } +} diff --git a/src/Console/stubs/perimeter.overlay.stub b/src/Console/stubs/perimeter.overlay.stub new file mode 100644 index 0000000..14470d7 --- /dev/null +++ b/src/Console/stubs/perimeter.overlay.stub @@ -0,0 +1,11 @@ +getControl()->should($user, __FUNCTION__, $model); } + + /** + * Determines if the specified user is authorized to restore the given model instance. + * + * @param Model $user The user attempting the restoration. + * @param Model $model The model instance to be restored. + * + * @return bool True if deletion is permitted, false otherwise. + */ + public function restore(Model $user, Model $model) + { + return $this->getControl()->should($user, __FUNCTION__, $model); + } + + /** + * Determines if the specified user is authorized to force delete the given model instance. + * + * @param Model $user The user attempting the force deletion. + * @param Model $model The model instance to be force deleted. + * + * @return bool True if deletion is permitted, false otherwise. + */ + public function forceDelete(Model $user, Model $model) + { + return $this->getControl()->should($user, __FUNCTION__, $model); + } } diff --git a/tests/Feature/ControlsQueryTest.php b/tests/Feature/ControlsQueryTest.php index d0acae0..4edf236 100644 --- a/tests/Feature/ControlsQueryTest.php +++ b/tests/Feature/ControlsQueryTest.php @@ -1,5 +1,7 @@ artisan('make:perimeter', ['name' => 'Perimeter']) + ->assertOk() + ->run(); + + $this->assertFileExists(app_path('Access/Perimeters/Perimeter.php')); + $this->assertStringContainsString('class Perimeter extends Perimeter', file_get_contents(app_path('Access/Perimeters/Perimeter.php'))); + + unlink(app_path('Access/Perimeters/Perimeter.php')); + } + + public function test_make_overlay_perimeter_command() + { + @unlink(app_path('Access/Perimeters/Perimeter.php')); + + $this + ->artisan('make:perimeter', ['name' => 'Perimeter', '--overlay' => true]) + ->assertOk() + ->run(); + + $this->assertFileExists(app_path('Access/Perimeters/Perimeter.php')); + $this->assertStringContainsString('class Perimeter extends OverlayPerimeter', file_get_contents(app_path('Access/Perimeters/Perimeter.php'))); + + unlink(app_path('Access/Perimeters/Perimeter.php')); + } + + public function test_make_control_command() + { + @unlink(app_path('Access/Controls/TestControl.php')); + + $this + ->artisan('make:control', ['name' => 'TestControl']) + ->assertOk() + ->run(); + + $this->assertFileExists(app_path('Access/Controls/TestControl.php')); + $this->assertStringContainsString('class TestControl', file_get_contents(app_path('Access/Controls/TestControl.php'))); + + unlink(app_path('Access/Controls/TestControl.php')); + } + + public function test_make_control_with_base_control_command() + { + @unlink(app_path('Access/Controls/TestControl.php')); + @unlink(app_path('Access/Controls/Control.php')); + + file_put_contents(app_path('Access/Controls/Control.php'), ''); + + $this + ->artisan('make:control', ['name' => 'TestControl']) + ->assertOk() + ->run(); + + $this->assertFileExists(app_path('Access/Controls/TestControl.php')); + $this->assertStringContainsString('class TestControl extends Control', file_get_contents(app_path('Access/Controls/TestControl.php'))); + + unlink(app_path('Access/Controls/TestControl.php')); + unlink(app_path('Access/Controls/Control.php')); + } + + public function test_make_control_with_perimeters_command() + { + @unlink(app_path('Access/Controls/TestControl.php')); + @unlink(app_path('Access/Perimeters/TestPerimeter.php')); + @unlink(app_path('Access/Perimeters/SecondTestPerimeter.php')); + + file_put_contents(app_path('Access/Perimeters/TestPerimeter.php'), ''); + file_put_contents(app_path('Access/Perimeters/SecondTestPerimeter.php'), ''); + + $this + ->artisan('make:control') + ->expectsQuestion('What should the control be named?', 'TestControl') + ->expectsChoice('What perimeters should this control apply to? (Optional)', ['TestPerimeter'], ['TestPerimeter', 'SecondTestPerimeter']) + ->assertOk() + ->run(); + + $this->assertFileExists(app_path('Access/Controls/TestControl.php')); + $this->assertStringContainsString('class TestControl', file_get_contents(app_path('Access/Controls/TestControl.php'))); + $this->assertStringContainsString(app_path('Access/Perimeters/TestPerimeter.php').'::new()', file_get_contents(app_path('Access/Controls/TestControl.php'))); + + unlink(app_path('Access/Perimeters/TestPerimeter.php')); + unlink(app_path('Access/Perimeters/SecondTestPerimeter.php')); + unlink(app_path('Access/Controls/TestControl.php')); + } +} \ No newline at end of file diff --git a/tests/Unit/StubsTest.php b/tests/Unit/StubsTest.php new file mode 100644 index 0000000..b1e5aab --- /dev/null +++ b/tests/Unit/StubsTest.php @@ -0,0 +1,22 @@ +dispatch($event = new \Illuminate\Foundation\Events\PublishingStubs([])); + + $this->assertEquals( + [ + '/app/src/Console/stubs/control.stub' => 'controller.stub', + '/app/src/Console/stubs/perimeter.plain.stub' => 'perimeter.plain.stub', + '/app/src/Console/stubs/perimeter.overlay.stub' => 'perimeter.overlay.stub' + ], + $event->stubs + ); + } +} \ No newline at end of file diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index 977e9e9..861fad7 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -1,5 +1,7 @@ Date: Wed, 9 Apr 2025 20:47:22 +0000 Subject: [PATCH 2/7] Apply fixes from StyleCI --- src/AccessServiceProvider.php | 10 ++++---- src/Console/ControlMakeCommand.php | 32 +++++++++++++------------ src/Console/PerimeterMakeCommand.php | 29 ++++++++++------------ tests/Unit/Console/MakeCommandsTest.php | 3 +-- tests/Unit/StubsTest.php | 8 +++---- 5 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/AccessServiceProvider.php b/src/AccessServiceProvider.php index daa1aab..4ef2667 100644 --- a/src/AccessServiceProvider.php +++ b/src/AccessServiceProvider.php @@ -11,7 +11,7 @@ class AccessServiceProvider extends ServiceProvider { protected array $devCommands = [ - 'ControlMake' => ControlMakeCommand::class, + 'ControlMake' => ControlMakeCommand::class, 'PerimeterMake' => PerimeterMakeCommand::class, ]; @@ -45,7 +45,8 @@ public function boot() /** * Register the given commands. * - * @param array $commands + * @param array $commands + * * @return void */ protected function registerCommands(array $commands) @@ -64,9 +65,10 @@ protected function registerCommands(array $commands) } /** - * Register the stubs on the default laravel stub publish command + * Register the stubs on the default laravel stub publish command. */ - protected function registerStubs() { + protected function registerStubs() + { Event::listen(function (PublishingStubs $event) { $event->add(realpath(__DIR__.'/Console/stubs/control.stub'), 'controller.stub'); $event->add(realpath(__DIR__.'/Console/stubs/perimeter.plain.stub'), 'perimeter.plain.stub'); diff --git a/src/Console/ControlMakeCommand.php b/src/Console/ControlMakeCommand.php index 94fd938..268e9bf 100644 --- a/src/Console/ControlMakeCommand.php +++ b/src/Console/ControlMakeCommand.php @@ -2,19 +2,14 @@ namespace Lomkit\Access\Console; -use Illuminate\Console\Concerns\CreatesMatchingTest; use Illuminate\Console\GeneratorCommand; use Illuminate\Support\Collection; -use Illuminate\Support\Str; -use InvalidArgumentException; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; - use Symfony\Component\Finder\Finder; -use function Laravel\Prompts\confirm; -use function Laravel\Prompts\select; + use function Laravel\Prompts\multiselect; #[AsCommand(name: 'make:control')] @@ -54,7 +49,8 @@ protected function getStub() /** * Resolve the fully-qualified path to the stub. * - * @param string $stub + * @param string $stub + * * @return string */ protected function resolveStubPath($stub) @@ -67,7 +63,8 @@ protected function resolveStubPath($stub) /** * Get the default namespace for the class. * - * @param string $rootNamespace + * @param string $rootNamespace + * * @return string */ protected function getDefaultNamespace($rootNamespace) @@ -80,7 +77,8 @@ protected function getDefaultNamespace($rootNamespace) * * Remove the base controller import if we are already in the base namespace. * - * @param string $name + * @param string $name + * * @return string */ protected function buildClass($name) @@ -102,15 +100,18 @@ protected function buildClass($name) } return str_replace( - array_keys($replace), array_values($replace), parent::buildClass($name) + array_keys($replace), + array_values($replace), + parent::buildClass($name) ); } /** * Build the model replacement values. * - * @param array $replace - * @param array $perimeters + * @param array $replace + * @param array $perimeters + * * @return array */ protected function buildPerimetersReplacements(array $replace, array $perimeters) @@ -137,7 +138,7 @@ protected function buildPerimetersReplacements(array $replace, array $perimeters return array_merge($replace, [ '{{ perimeters }}' => $perimetersImplementation, - '{{perimeters}}' => $perimetersImplementation, + '{{perimeters}}' => $perimetersImplementation, ]); } @@ -156,8 +157,9 @@ protected function getOptions() /** * Interact further with the user if they were prompted for missing arguments. * - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * * @return void */ protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) diff --git a/src/Console/PerimeterMakeCommand.php b/src/Console/PerimeterMakeCommand.php index 6a6e906..020c2e1 100644 --- a/src/Console/PerimeterMakeCommand.php +++ b/src/Console/PerimeterMakeCommand.php @@ -2,21 +2,12 @@ namespace Lomkit\Access\Console; -use Illuminate\Console\Concerns\CreatesMatchingTest; use Illuminate\Console\GeneratorCommand; -use Illuminate\Support\Collection; -use Illuminate\Support\Str; -use InvalidArgumentException; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Finder\Finder; -use function Laravel\Prompts\confirm; -use function Laravel\Prompts\select; -use function Laravel\Prompts\multiselect; - #[AsCommand(name: 'make:perimeter')] class PerimeterMakeCommand extends GeneratorCommand { @@ -51,7 +42,7 @@ protected function getStub() $stub = null; if ($this->option('overlay')) { - $stub = "/stubs/perimeter.overlay.stub"; + $stub = '/stubs/perimeter.overlay.stub'; } $stub ??= '/stubs/perimeter.plain.stub'; @@ -62,7 +53,8 @@ protected function getStub() /** * Resolve the fully-qualified path to the stub. * - * @param string $stub + * @param string $stub + * * @return string */ protected function resolveStubPath($stub) @@ -75,7 +67,8 @@ protected function resolveStubPath($stub) /** * Get the default namespace for the class. * - * @param string $rootNamespace + * @param string $rootNamespace + * * @return string */ protected function getDefaultNamespace($rootNamespace) @@ -88,7 +81,8 @@ protected function getDefaultNamespace($rootNamespace) * * Remove the base controller import if we are already in the base namespace. * - * @param string $name + * @param string $name + * * @return string */ protected function buildClass($name) @@ -96,7 +90,9 @@ protected function buildClass($name) $replace = []; return str_replace( - array_keys($replace), array_values($replace), parent::buildClass($name) + array_keys($replace), + array_values($replace), + parent::buildClass($name) ); } @@ -115,8 +111,9 @@ protected function getOptions() /** * Interact further with the user if they were prompted for missing arguments. * - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * * @return void */ protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) diff --git a/tests/Unit/Console/MakeCommandsTest.php b/tests/Unit/Console/MakeCommandsTest.php index 9ac55de..ab62139 100644 --- a/tests/Unit/Console/MakeCommandsTest.php +++ b/tests/Unit/Console/MakeCommandsTest.php @@ -2,7 +2,6 @@ namespace Lomkit\Access\Tests\Unit\Console; -use Illuminate\Support\Facades\Artisan; use Lomkit\Access\Tests\Unit\TestCase; class MakeCommandsTest extends TestCase @@ -95,4 +94,4 @@ public function test_make_control_with_perimeters_command() unlink(app_path('Access/Perimeters/SecondTestPerimeter.php')); unlink(app_path('Access/Controls/TestControl.php')); } -} \ No newline at end of file +} diff --git a/tests/Unit/StubsTest.php b/tests/Unit/StubsTest.php index b1e5aab..b793ea5 100644 --- a/tests/Unit/StubsTest.php +++ b/tests/Unit/StubsTest.php @@ -12,11 +12,11 @@ public function test_stubs_correctly_registered(): void $this->assertEquals( [ - '/app/src/Console/stubs/control.stub' => 'controller.stub', - '/app/src/Console/stubs/perimeter.plain.stub' => 'perimeter.plain.stub', - '/app/src/Console/stubs/perimeter.overlay.stub' => 'perimeter.overlay.stub' + '/app/src/Console/stubs/control.stub' => 'controller.stub', + '/app/src/Console/stubs/perimeter.plain.stub' => 'perimeter.plain.stub', + '/app/src/Console/stubs/perimeter.overlay.stub' => 'perimeter.overlay.stub', ], $event->stubs ); } -} \ No newline at end of file +} From 6611afcab9e0c0b114e6a56d2119bf974292de51 Mon Sep 17 00:00:00 2001 From: Gautier DELEGLISE Date: Wed, 9 Apr 2025 22:57:42 +0200 Subject: [PATCH 3/7] Update StubsTest.php --- tests/Unit/StubsTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Unit/StubsTest.php b/tests/Unit/StubsTest.php index b793ea5..b19e391 100644 --- a/tests/Unit/StubsTest.php +++ b/tests/Unit/StubsTest.php @@ -9,12 +9,12 @@ class StubsTest extends TestCase public function test_stubs_correctly_registered(): void { app(Dispatcher::class)->dispatch($event = new \Illuminate\Foundation\Events\PublishingStubs([])); - + $this->assertEquals( [ - '/app/src/Console/stubs/control.stub' => 'controller.stub', - '/app/src/Console/stubs/perimeter.plain.stub' => 'perimeter.plain.stub', - '/app/src/Console/stubs/perimeter.overlay.stub' => 'perimeter.overlay.stub', + realpath(__DIR__.'/../../src/Console/stubs/control.stub') => 'controller.stub', + realpath(__DIR__.'/../../src/Console/stubs/perimeter.plain.stub') => 'perimeter.plain.stub', + realpath(__DIR__.'/../../src/Console/stubs/perimeter.overlay.stub') => 'perimeter.overlay.stub', ], $event->stubs ); From 7203efa17487d5378d0d683387564b1574163983 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 9 Apr 2025 20:58:01 +0000 Subject: [PATCH 4/7] Apply fixes from StyleCI --- tests/Unit/StubsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/StubsTest.php b/tests/Unit/StubsTest.php index b19e391..7d9b40b 100644 --- a/tests/Unit/StubsTest.php +++ b/tests/Unit/StubsTest.php @@ -9,7 +9,7 @@ class StubsTest extends TestCase public function test_stubs_correctly_registered(): void { app(Dispatcher::class)->dispatch($event = new \Illuminate\Foundation\Events\PublishingStubs([])); - + $this->assertEquals( [ realpath(__DIR__.'/../../src/Console/stubs/control.stub') => 'controller.stub', From 4c496f7284403638be629fed24afe9f8235b4ec5 Mon Sep 17 00:00:00 2001 From: Gautier DELEGLISE Date: Thu, 10 Apr 2025 20:45:47 +0200 Subject: [PATCH 5/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Console/ControlMakeCommand.php | 2 +- src/Console/PerimeterMakeCommand.php | 9 ++++++++- src/Console/stubs/control.stub | 3 ++- src/Controls/Control.php | 2 +- src/Policies/ControlledPolicy.php | 4 ++-- tests/Feature/ControlsShouldTest.php | 2 +- tests/Unit/Console/MakeCommandsTest.php | 22 +++++++++++----------- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Console/ControlMakeCommand.php b/src/Console/ControlMakeCommand.php index 268e9bf..e65a904 100644 --- a/src/Console/ControlMakeCommand.php +++ b/src/Console/ControlMakeCommand.php @@ -119,7 +119,7 @@ protected function buildPerimetersReplacements(array $replace, array $perimeters $perimetersImplementation = ''; foreach ($perimeters as $perimeter) { - $perimeterClass = app_path('Access/Perimeters/'.$perimeter.'.php'); + $perimeterClass = $this->rootNamespace().'Access\\Perimeters\\'.$perimeter.'.php'; $perimetersImplementation .= <<didReceiveOptions($input)) { + return; + } + + if (confirm('Should this perimeter be an overlay?', false)) { + $input->setOption('overlay', true); + } } } diff --git a/src/Console/stubs/control.stub b/src/Console/stubs/control.stub index 55424b6..ff42a08 100644 --- a/src/Console/stubs/control.stub +++ b/src/Console/stubs/control.stub @@ -4,6 +4,7 @@ namespace {{ namespace }}; use {{ rootNamespace }}Http\Controllers\Controller; use Illuminate\Http\Request; +use Lomkit\Access\Controls\Control; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; @@ -12,7 +13,7 @@ class {{ class }} extends Control /** * Retrieve the list of perimeter definitions for the current control. * - * @return array An array of Perimeter objects. + * @return array<\Lomkit\Access\Perimeters\Perimeter> An array of Perimeter objects. */ protected function perimeters(): array { diff --git a/src/Controls/Control.php b/src/Controls/Control.php index 50a8283..8ed11b8 100644 --- a/src/Controls/Control.php +++ b/src/Controls/Control.php @@ -30,7 +30,7 @@ class Control /** * Retrieve the list of perimeter definitions for the current control. * - * @return array An array of Perimeter objects. + * @return array<\Lomkit\Access\Perimeters\Perimeter> An array of Perimeter objects. */ protected function perimeters(): array { diff --git a/src/Policies/ControlledPolicy.php b/src/Policies/ControlledPolicy.php index 69b6115..0e3b0f0 100644 --- a/src/Policies/ControlledPolicy.php +++ b/src/Policies/ControlledPolicy.php @@ -105,7 +105,7 @@ public function delete(Model $user, Model $model) * @param Model $user The user attempting the restoration. * @param Model $model The model instance to be restored. * - * @return bool True if deletion is permitted, false otherwise. + * @return bool True if restoration is permitted, false otherwise. */ public function restore(Model $user, Model $model) { @@ -118,7 +118,7 @@ public function restore(Model $user, Model $model) * @param Model $user The user attempting the force deletion. * @param Model $model The model instance to be force deleted. * - * @return bool True if deletion is permitted, false otherwise. + * @return bool True if force deletion is permitted, false otherwise. */ public function forceDelete(Model $user, Model $model) { diff --git a/tests/Feature/ControlsShouldTest.php b/tests/Feature/ControlsShouldTest.php index 7f38033..af6c883 100644 --- a/tests/Feature/ControlsShouldTest.php +++ b/tests/Feature/ControlsShouldTest.php @@ -140,7 +140,7 @@ public function test_control_should_delete_global_using_shared_overlayed_perimet $this->assertTrue((new \Lomkit\Access\Tests\Support\Access\Controls\ModelControl())->applies(Auth::user(), 'delete', $model)); } - public function test_control_should_not_delete_global_using_shared_overlayed_perimeter(): void + public function test_control_should_delete_using_shared_overlayed_perimeter(): void { Auth::user()->update(['should_shared' => true]); Auth::user()->update(['should_global' => true]); diff --git a/tests/Unit/Console/MakeCommandsTest.php b/tests/Unit/Console/MakeCommandsTest.php index ab62139..5db2037 100644 --- a/tests/Unit/Console/MakeCommandsTest.php +++ b/tests/Unit/Console/MakeCommandsTest.php @@ -8,32 +8,32 @@ class MakeCommandsTest extends TestCase { public function test_make_plain_perimeter_command() { - @unlink(app_path('Access/Perimeters/Perimeter.php')); + @unlink(app_path('Access/Perimeters/TestPerimeter.php')); $this - ->artisan('make:perimeter', ['name' => 'Perimeter']) + ->artisan('make:perimeter', ['name' => 'TestPerimeter']) ->assertOk() ->run(); - $this->assertFileExists(app_path('Access/Perimeters/Perimeter.php')); - $this->assertStringContainsString('class Perimeter extends Perimeter', file_get_contents(app_path('Access/Perimeters/Perimeter.php'))); + $this->assertFileExists(app_path('Access/Perimeters/TestPerimeter.php')); + $this->assertStringContainsString('class TestPerimeter extends Perimeter', file_get_contents(app_path('Access/Perimeters/TestPerimeter.php'))); - unlink(app_path('Access/Perimeters/Perimeter.php')); + unlink(app_path('Access/Perimeters/TestPerimeter.php')); } public function test_make_overlay_perimeter_command() { - @unlink(app_path('Access/Perimeters/Perimeter.php')); + @unlink(app_path('Access/Perimeters/TestPerimeter.php')); $this - ->artisan('make:perimeter', ['name' => 'Perimeter', '--overlay' => true]) + ->artisan('make:perimeter', ['name' => 'TestPerimeter', '--overlay' => true]) ->assertOk() ->run(); - $this->assertFileExists(app_path('Access/Perimeters/Perimeter.php')); - $this->assertStringContainsString('class Perimeter extends OverlayPerimeter', file_get_contents(app_path('Access/Perimeters/Perimeter.php'))); + $this->assertFileExists(app_path('Access/Perimeters/TestPerimeter.php')); + $this->assertStringContainsString('class TestPerimeter extends OverlayPerimeter', file_get_contents(app_path('Access/Perimeters/TestPerimeter.php'))); - unlink(app_path('Access/Perimeters/Perimeter.php')); + unlink(app_path('Access/Perimeters/TestPerimeter.php')); } public function test_make_control_command() @@ -88,7 +88,7 @@ public function test_make_control_with_perimeters_command() $this->assertFileExists(app_path('Access/Controls/TestControl.php')); $this->assertStringContainsString('class TestControl', file_get_contents(app_path('Access/Controls/TestControl.php'))); - $this->assertStringContainsString(app_path('Access/Perimeters/TestPerimeter.php').'::new()', file_get_contents(app_path('Access/Controls/TestControl.php'))); + $this->assertStringContainsString('App\\Access\\Perimeters\\TestPerimeter.php::new()', file_get_contents(app_path('Access/Controls/TestControl.php'))); unlink(app_path('Access/Perimeters/TestPerimeter.php')); unlink(app_path('Access/Perimeters/SecondTestPerimeter.php')); From 16b44837c02aa5c12545f9da6c6cbc3b83875a4e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 10 Apr 2025 18:46:07 +0000 Subject: [PATCH 6/7] Apply fixes from StyleCI --- src/Console/PerimeterMakeCommand.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Console/PerimeterMakeCommand.php b/src/Console/PerimeterMakeCommand.php index 177ec01..9a5b025 100644 --- a/src/Console/PerimeterMakeCommand.php +++ b/src/Console/PerimeterMakeCommand.php @@ -7,6 +7,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; + use function Laravel\Prompts\confirm; #[AsCommand(name: 'make:perimeter')] @@ -119,12 +120,12 @@ protected function getOptions() */ protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output) { - if ($this->didReceiveOptions($input)) { - return; - } + if ($this->didReceiveOptions($input)) { + return; + } - if (confirm('Should this perimeter be an overlay?', false)) { - $input->setOption('overlay', true); - } + if (confirm('Should this perimeter be an overlay?', false)) { + $input->setOption('overlay', true); + } } } From 83f14bab5cbe7870d9007e30609b806d441bc58f Mon Sep 17 00:00:00 2001 From: Gautier DELEGLISE Date: Thu, 10 Apr 2025 20:54:36 +0200 Subject: [PATCH 7/7] =?UTF-8?q?=E2=9C=85=20small=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Console/ControlMakeCommand.php | 2 +- tests/Unit/Console/MakeCommandsTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/ControlMakeCommand.php b/src/Console/ControlMakeCommand.php index e65a904..e9d8b7d 100644 --- a/src/Console/ControlMakeCommand.php +++ b/src/Console/ControlMakeCommand.php @@ -119,7 +119,7 @@ protected function buildPerimetersReplacements(array $replace, array $perimeters $perimetersImplementation = ''; foreach ($perimeters as $perimeter) { - $perimeterClass = $this->rootNamespace().'Access\\Perimeters\\'.$perimeter.'.php'; + $perimeterClass = $this->rootNamespace().'Access\\Perimeters\\'.$perimeter; $perimetersImplementation .= <<assertFileExists(app_path('Access/Controls/TestControl.php')); $this->assertStringContainsString('class TestControl', file_get_contents(app_path('Access/Controls/TestControl.php'))); - $this->assertStringContainsString('App\\Access\\Perimeters\\TestPerimeter.php::new()', file_get_contents(app_path('Access/Controls/TestControl.php'))); + $this->assertStringContainsString('App\\Access\\Perimeters\\TestPerimeter::new()', file_get_contents(app_path('Access/Controls/TestControl.php'))); unlink(app_path('Access/Perimeters/TestPerimeter.php')); unlink(app_path('Access/Perimeters/SecondTestPerimeter.php'));