From 0dcab74337dc2904868768cb746d32f030763624 Mon Sep 17 00:00:00 2001 From: Tina Hammar Date: Fri, 31 Jan 2025 19:35:25 +0100 Subject: [PATCH 01/10] optionally save existing app key --- .../Foundation/Console/KeyGenerateCommand.php | 42 +++++++-- .../Foundation/Console/RotateKeyCommand.php | 93 +++++++++++++++++++ 2 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 src/Illuminate/Foundation/Console/RotateKeyCommand.php diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index d51fce79f45e..615725c614e9 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -33,12 +33,17 @@ class KeyGenerateCommand extends Command * * @return void */ - public function handle() + public function handle(): void { + if ($this->shouldSaveExistingKey()) { + return; + } + $key = $this->generateRandomKey(); if ($this->option('show')) { - return $this->line(''.$key.''); + $this->line(''.$key.''); + return; } // Next, we will replace the application key in the environment file so it is @@ -53,12 +58,33 @@ public function handle() $this->components->info('Application key set successfully.'); } + + /** + * Ask the user if they want to save the existing key. + */ + protected function shouldSaveExistingKey(): bool + { + $currentKey = $this->laravel['config']['app.key']; + + if (! empty($currentKey) && $this->confirm( + 'There is already an app key. Do you want to store the old key before generating a new one?', + true + )) { + + $this->call('key:rotate'); + + return true; + } + + return false; + } + /** * Generate a random key for the application. * * @return string */ - protected function generateRandomKey() + protected function generateRandomKey(): string { return 'base64:'.base64_encode( Encrypter::generateKey($this->laravel['config']['app.cipher']) @@ -68,10 +94,10 @@ protected function generateRandomKey() /** * Set the application key in the environment file. * - * @param string $key + * @param string $key * @return bool */ - protected function setKeyInEnvironmentFile($key) + protected function setKeyInEnvironmentFile(string $key): bool { $currentKey = $this->laravel['config']['app.key']; @@ -89,10 +115,10 @@ protected function setKeyInEnvironmentFile($key) /** * Write a new environment file with the given key. * - * @param string $key + * @param string $key * @return bool */ - protected function writeNewEnvironmentFileWith($key) + protected function writeNewEnvironmentFileWith(string $key): bool { $replaced = preg_replace( $this->keyReplacementPattern(), @@ -116,7 +142,7 @@ protected function writeNewEnvironmentFileWith($key) * * @return string */ - protected function keyReplacementPattern() + protected function keyReplacementPattern(): string { $escaped = preg_quote('='.$this->laravel['config']['app.key'], '/'); diff --git a/src/Illuminate/Foundation/Console/RotateKeyCommand.php b/src/Illuminate/Foundation/Console/RotateKeyCommand.php new file mode 100644 index 000000000000..1dd520d05b07 --- /dev/null +++ b/src/Illuminate/Foundation/Console/RotateKeyCommand.php @@ -0,0 +1,93 @@ +laravel['config']['app.key']; + + // 2. Get current APP_PREVIOUS_KEYS as array and prepend the current key + $previousKeys = Arr::prepend($this->laravel['config']['app.previous_keys'] ?? [], $currentKey); + + // 3. Update .env file with the new APP_PREVIOUS_KEYS and clear APP_KEY + if (! $this->updateEnvFile($currentKey, $previousKeys)) { + $this->components->error('Failed to update the environment file.'); + + return; + } + + // 4. Notify the user and generate a new key + $this->components->info('Current application key has been saved successfully. Running php artisan key:generate to generate a new key.'); + $this->call('key:generate'); + $this->components->info('Application key has been rotated successfully.'); + } + + /** + * Update the .env file with the new APP_PREVIOUS_KEYS and clear APP_KEY. + */ + protected function updateEnvFile(string $currentKey, array $previousKeys): bool + { + $envPath = $this->laravel->environmentFilePath(); + $contents = file_get_contents($envPath); + + // Convert array to comma-separated string and wrap in quotes + $quotedPreviousKeys = '"' . implode(',', $previousKeys) . '"'; + + // Update APP_PREVIOUS_KEYS + if (str_contains($contents, 'APP_PREVIOUS_KEYS=')) { + // If APP_PREVIOUS_KEYS already exists, update its value + $contents = preg_replace($this->previousKeysPattern(), 'APP_PREVIOUS_KEYS=' . $quotedPreviousKeys, $contents); + } else { + // If APP_PREVIOUS_KEYS does not exist, insert it after APP_KEY + $contents = preg_replace($this->keyPattern(), "APP_KEY=\nAPP_PREVIOUS_KEYS=" . $quotedPreviousKeys, $contents); + } + + // Clear APP_KEY + $contents = preg_replace($this->keyPattern(), 'APP_KEY=', $contents); + $this->laravel['config']['app.key'] = null; + + return file_put_contents($envPath, $contents) !== false; + } + + /** + * Generate a regex pattern to match the current APP_KEY line + */ + protected function keyPattern(): string + { + return '/^APP_KEY=' . preg_quote($this->laravel['config']['app.key'], '/') . '/m'; + } + + /** + * Generate a regex pattern to match the current APP_PREVIOUS_KEYS line + */ + protected function previousKeysPattern(): string + { + return '/^APP_PREVIOUS_KEYS="' . preg_quote(implode(',', (array) $this->laravel['config']['app.previous_keys']), '/') . '"/m'; + } +} From d84f03a13737ebc78159232998f68284c3ac7f5d Mon Sep 17 00:00:00 2001 From: Tina Hammar Date: Fri, 31 Jan 2025 20:11:41 +0100 Subject: [PATCH 02/10] trying to fix code style errors --- .../Foundation/Console/KeyGenerateCommand.php | 1 - .../Foundation/Console/RotateKeyCommand.php | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 615725c614e9..bae17b40a478 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -70,7 +70,6 @@ protected function shouldSaveExistingKey(): bool 'There is already an app key. Do you want to store the old key before generating a new one?', true )) { - $this->call('key:rotate'); return true; diff --git a/src/Illuminate/Foundation/Console/RotateKeyCommand.php b/src/Illuminate/Foundation/Console/RotateKeyCommand.php index 1dd520d05b07..5bc017468918 100644 --- a/src/Illuminate/Foundation/Console/RotateKeyCommand.php +++ b/src/Illuminate/Foundation/Console/RotateKeyCommand.php @@ -57,15 +57,15 @@ protected function updateEnvFile(string $currentKey, array $previousKeys): bool $contents = file_get_contents($envPath); // Convert array to comma-separated string and wrap in quotes - $quotedPreviousKeys = '"' . implode(',', $previousKeys) . '"'; + $quotedPreviousKeys = '"'.implode(',', $previousKeys).'"'; // Update APP_PREVIOUS_KEYS if (str_contains($contents, 'APP_PREVIOUS_KEYS=')) { // If APP_PREVIOUS_KEYS already exists, update its value - $contents = preg_replace($this->previousKeysPattern(), 'APP_PREVIOUS_KEYS=' . $quotedPreviousKeys, $contents); + $contents = preg_replace($this->previousKeysPattern(), 'APP_PREVIOUS_KEYS='.$quotedPreviousKeys, $contents); } else { // If APP_PREVIOUS_KEYS does not exist, insert it after APP_KEY - $contents = preg_replace($this->keyPattern(), "APP_KEY=\nAPP_PREVIOUS_KEYS=" . $quotedPreviousKeys, $contents); + $contents = preg_replace($this->keyPattern(), "APP_KEY=\nAPP_PREVIOUS_KEYS=".$quotedPreviousKeys, $contents); } // Clear APP_KEY @@ -76,18 +76,18 @@ protected function updateEnvFile(string $currentKey, array $previousKeys): bool } /** - * Generate a regex pattern to match the current APP_KEY line + * Generate a regex pattern to match the current APP_KEY line. */ protected function keyPattern(): string { - return '/^APP_KEY=' . preg_quote($this->laravel['config']['app.key'], '/') . '/m'; + return '/^APP_KEY='.preg_quote($this->laravel['config']['app.key'], '/').'/m'; } /** - * Generate a regex pattern to match the current APP_PREVIOUS_KEYS line + * Generate a regex pattern to match the current APP_PREVIOUS_KEYS line. */ protected function previousKeysPattern(): string { - return '/^APP_PREVIOUS_KEYS="' . preg_quote(implode(',', (array) $this->laravel['config']['app.previous_keys']), '/') . '"/m'; + return '/^APP_PREVIOUS_KEYS="'.preg_quote(implode(',', (array) $this->laravel['config']['app.previous_keys']), '/').'"/m'; } } From ac3d761fbaea17a9c1215a632605b1752fb56c19 Mon Sep 17 00:00:00 2001 From: Tina Hammar Date: Fri, 31 Jan 2025 20:24:50 +0100 Subject: [PATCH 03/10] Apply patch to resolve style errors --- .../Foundation/Console/KeyGenerateCommand.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index bae17b40a478..9cbd594a7f3b 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -43,6 +43,7 @@ public function handle(): void if ($this->option('show')) { $this->line(''.$key.''); + return; } @@ -58,7 +59,6 @@ public function handle(): void $this->components->info('Application key set successfully.'); } - /** * Ask the user if they want to save the existing key. */ @@ -67,9 +67,9 @@ protected function shouldSaveExistingKey(): bool $currentKey = $this->laravel['config']['app.key']; if (! empty($currentKey) && $this->confirm( - 'There is already an app key. Do you want to store the old key before generating a new one?', - true - )) { + 'There is already an app key. Do you want to store the old key before generating a new one?', + true + )) { $this->call('key:rotate'); return true; @@ -93,7 +93,7 @@ protected function generateRandomKey(): string /** * Set the application key in the environment file. * - * @param string $key + * @param string $key * @return bool */ protected function setKeyInEnvironmentFile(string $key): bool @@ -114,7 +114,7 @@ protected function setKeyInEnvironmentFile(string $key): bool /** * Write a new environment file with the given key. * - * @param string $key + * @param string $key * @return bool */ protected function writeNewEnvironmentFileWith(string $key): bool From 8886eaf4ada31d75349e66e75046104359fd0e34 Mon Sep 17 00:00:00 2001 From: TinaH Date: Sat, 1 Feb 2025 21:35:30 +0100 Subject: [PATCH 04/10] Update KeyGenerateCommand.php Co-authored-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 9cbd594a7f3b..70cc8e04d50c 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -141,7 +141,7 @@ protected function writeNewEnvironmentFileWith(string $key): bool * * @return string */ - protected function keyReplacementPattern(): string + protected function keyReplacementPattern() { $escaped = preg_quote('='.$this->laravel['config']['app.key'], '/'); From 6f076201dd7f017c39cb93152a6f65021e0e651d Mon Sep 17 00:00:00 2001 From: TinaH Date: Sat, 1 Feb 2025 21:35:41 +0100 Subject: [PATCH 05/10] Update KeyGenerateCommand.php Co-authored-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 70cc8e04d50c..afe2b649e7e4 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -117,7 +117,7 @@ protected function setKeyInEnvironmentFile(string $key): bool * @param string $key * @return bool */ - protected function writeNewEnvironmentFileWith(string $key): bool + protected function writeNewEnvironmentFileWith($key) { $replaced = preg_replace( $this->keyReplacementPattern(), From bf3522fd083246ef3590a9cd37d0b44bc5055a94 Mon Sep 17 00:00:00 2001 From: TinaH Date: Sat, 1 Feb 2025 21:35:50 +0100 Subject: [PATCH 06/10] Update KeyGenerateCommand.php Co-authored-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index afe2b649e7e4..90ebb680c8bf 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -96,7 +96,7 @@ protected function generateRandomKey(): string * @param string $key * @return bool */ - protected function setKeyInEnvironmentFile(string $key): bool + protected function setKeyInEnvironmentFile($key) { $currentKey = $this->laravel['config']['app.key']; From 76ddee753f03373045c959ac2f06ab188c863195 Mon Sep 17 00:00:00 2001 From: TinaH Date: Sat, 1 Feb 2025 21:35:57 +0100 Subject: [PATCH 07/10] Update KeyGenerateCommand.php Co-authored-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 90ebb680c8bf..4aab800c3bec 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -83,7 +83,7 @@ protected function shouldSaveExistingKey(): bool * * @return string */ - protected function generateRandomKey(): string + protected function generateRandomKey() { return 'base64:'.base64_encode( Encrypter::generateKey($this->laravel['config']['app.cipher']) From ea1426ffa5f4763c6c140745aaab6d9f006240a2 Mon Sep 17 00:00:00 2001 From: TinaH Date: Sat, 1 Feb 2025 21:36:06 +0100 Subject: [PATCH 08/10] Update KeyGenerateCommand.php Co-authored-by: Mior Muhammad Zaki --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 4aab800c3bec..fd6f34f6ce81 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -33,7 +33,7 @@ class KeyGenerateCommand extends Command * * @return void */ - public function handle(): void + public function handle() { if ($this->shouldSaveExistingKey()) { return; From 29c1c4cb12a1412b26b2608e2ac4d1627c76c679 Mon Sep 17 00:00:00 2001 From: Tina Hammar Date: Mon, 3 Feb 2025 14:11:37 +0100 Subject: [PATCH 09/10] resolve comment --- src/Illuminate/Foundation/Console/KeyGenerateCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php index 9cbd594a7f3b..d4a32572d5ef 100644 --- a/src/Illuminate/Foundation/Console/KeyGenerateCommand.php +++ b/src/Illuminate/Foundation/Console/KeyGenerateCommand.php @@ -36,6 +36,8 @@ class KeyGenerateCommand extends Command public function handle(): void { if ($this->shouldSaveExistingKey()) { + $this->call('key:rotate'); + return; } @@ -70,8 +72,6 @@ protected function shouldSaveExistingKey(): bool 'There is already an app key. Do you want to store the old key before generating a new one?', true )) { - $this->call('key:rotate'); - return true; } From 68accdafa4d12c3e48817d36874e3cb9856426e0 Mon Sep 17 00:00:00 2001 From: Tina Hammar Date: Mon, 3 Feb 2025 14:15:06 +0100 Subject: [PATCH 10/10] force new key to avoid empty state --- src/Illuminate/Foundation/Console/RotateKeyCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Illuminate/Foundation/Console/RotateKeyCommand.php b/src/Illuminate/Foundation/Console/RotateKeyCommand.php index 5bc017468918..0e36df468aa8 100644 --- a/src/Illuminate/Foundation/Console/RotateKeyCommand.php +++ b/src/Illuminate/Foundation/Console/RotateKeyCommand.php @@ -43,8 +43,8 @@ public function handle(): void } // 4. Notify the user and generate a new key - $this->components->info('Current application key has been saved successfully. Running php artisan key:generate to generate a new key.'); - $this->call('key:generate'); + $this->components->info('Current application key has been saved successfully. Running php artisan key:generate --force to generate a new key.'); + $this->call('key:generate', ['--force' => true]); $this->components->info('Application key has been rotated successfully.'); }