From 10ca2d4fe6a109fc0f41363d7be416fe4e6c2321 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Mon, 13 Jan 2025 15:45:20 -0500 Subject: [PATCH 1/2] feat: generate key command --- src/Console/Commands/GenerateKey.php | 75 +++++++++++++++++++ src/Providers/CommandsServiceProvider.php | 2 + src/Testing/TestCase.php | 2 +- tests/Unit/Console/GenerateKeyCommandTest.php | 73 ++++++++++++++++++ tests/fixtures/application/config/app.php | 1 + 5 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/Console/Commands/GenerateKey.php create mode 100644 tests/Unit/Console/GenerateKeyCommandTest.php diff --git a/src/Console/Commands/GenerateKey.php b/src/Console/Commands/GenerateKey.php new file mode 100644 index 0000000..1e10ea2 --- /dev/null +++ b/src/Console/Commands/GenerateKey.php @@ -0,0 +1,75 @@ +setHelp('This command allows you to generate and set the application key.'); + + $this->addArgument('environment', InputArgument::OPTIONAL, 'The environment file', '.env'); + + $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Force to create queries'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $force = $input->getOption('force'); + + $currentKey = Config::get('app.key'); + + if ($currentKey && ! $force) { + $output->writeln('Application key is already set. Use --force to override it.'); + + return Command::FAILURE; + } + + $key = 'base64:' . base64_encode(random_bytes(32)); + $environment = $input->getArgument('environment'); + + $environmentData = File::get(base_path($environment)); + + $replaced = preg_replace( + "/^APP_KEY=.*$/m", + "APP_KEY={$key}", + $environmentData + ); + + if (empty($replaced) || $replaced === $environmentData) { + $output->writeln('Failed to set the application key.'); + + return Command::FAILURE; + } + + File::put(base_path($environment), $replaced); + + $output->writeln('Application key set successfully!.'); + + return Command::SUCCESS; + } +} diff --git a/src/Providers/CommandsServiceProvider.php b/src/Providers/CommandsServiceProvider.php index 6b940c2..f5bf308 100644 --- a/src/Providers/CommandsServiceProvider.php +++ b/src/Providers/CommandsServiceProvider.php @@ -4,6 +4,7 @@ namespace Phenix\Providers; +use Phenix\Console\Commands\GenerateKey; use Phenix\Console\Commands\MakeCollection; use Phenix\Console\Commands\MakeController; use Phenix\Console\Commands\MakeMiddleware; @@ -26,6 +27,7 @@ public function boot(): void MakeCollection::class, MakeQuery::class, MakeServiceProvider::class, + GenerateKey::class, ]); } } diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index ea8f472..7c64c48 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -38,7 +38,7 @@ protected function tearDown(): void $this->app = null; } - protected function phenix(string $signature, array $arguments, array $inputs = []): CommandTester + protected function phenix(string $signature, array $arguments = [], array $inputs = []): CommandTester { $phenix = App::make(Phenix::class); diff --git a/tests/Unit/Console/GenerateKeyCommandTest.php b/tests/Unit/Console/GenerateKeyCommandTest.php new file mode 100644 index 0000000..b61350b --- /dev/null +++ b/tests/Unit/Console/GenerateKeyCommandTest.php @@ -0,0 +1,73 @@ +expect( + get: function (string $path): string { + return 'APP_KEY=' . PHP_EOL; + }, + put: fn (string $path) => true, + ); + + $this->app->swap(File::class, $mock); + + /** @var CommandTester $command */ + $command = $this->phenix('key:generate'); + + $command->assertCommandIsSuccessful(); + + expect($command->getDisplay())->toContain('Application key set successfully!'); +}); + +it('does not set the application key if it is already set', function () { + Config::set('app.key', 'base64:' . base64_encode(random_bytes(32))); + + /** @var CommandTester $command */ + $command = $this->phenix('key:generate'); + + expect($command->getStatusCode())->toBe(Command::FAILURE); + expect($command->getDisplay())->toContain('Application key is already set. Use --force to override it.'); +}); + +it('fails on set application key', function () { + $mock = Mock::of(File::class)->expect( + get: fn (string $path): string => '', + ); + + $this->app->swap(File::class, $mock); + + /** @var CommandTester $command */ + $command = $this->phenix('key:generate'); + + expect($command->getStatusCode())->toBe(Command::FAILURE); + expect($command->getDisplay())->toContain('Failed to set the application key'); +}); + +it('sets application key with force option', function () { + Config::set('app.key', 'base64:' . base64_encode(random_bytes(32))); + + $mock = Mock::of(File::class)->expect( + get: function (string $path): string { + return 'APP_KEY=' . PHP_EOL; + }, + put: fn (string $path) => true, + ); + + $this->app->swap(File::class, $mock); + + /** @var CommandTester $command */ + $command = $this->phenix('key:generate', [ + '--force' => true, + ]); + + $command->assertCommandIsSuccessful(); + + expect($command->getDisplay())->toContain('Application key set successfully!'); +}); diff --git a/tests/fixtures/application/config/app.php b/tests/fixtures/application/config/app.php index 86f40cd..3995858 100644 --- a/tests/fixtures/application/config/app.php +++ b/tests/fixtures/application/config/app.php @@ -7,6 +7,7 @@ 'env' => env('APP_ENV', fn () => 'local'), 'url' => env('APP_URL', fn () => 'http://127.0.0.1'), 'port' => env('APP_PORT', fn () => 1337), + 'key' => env('APP_KEY'), 'middlewares' => [ 'global' => [ \Phenix\Http\Middlewares\HandleCors::class, From edb0aed25a977f82bceb2a2e0ac77d4a25b8aaa6 Mon Sep 17 00:00:00 2001 From: Omar Barbosa Date: Mon, 13 Jan 2025 15:45:37 -0500 Subject: [PATCH 2/2] docs: add credits section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8ea465b..3087e75 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,8 @@ If you discover a security vulnerability within Phenix, please send an e-mail to ## License The Phenix framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). + +## Credits + +- [Amphp team](https://amphp.org/) +- [Laravel team](https://laravel.com/)