Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9.x] New env:encrypt and env:decrypt commands #44034

Merged
merged 11 commits into from
Sep 26, 2022
147 changes: 147 additions & 0 deletions src/Illuminate/Foundation/Console/EnvironmentDecryptCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

namespace Illuminate\Foundation\Console;

use Exception;
use Illuminate\Console\Command;
use Illuminate\Encryption\Encrypter;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Env;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'env:decrypt')]
class EnvironmentDecryptCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'env:decrypt
{--key= : The encryption key}
{--cipher= : The encryption cipher}
{--env= : The environment to be decrypted}
{--force : Overwrite the existing environment file}
{--filename= : Where to write the decrypted file contents}';

/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'env:decrypt';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Decrypt an environment file';

/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;

/**
* Create a new command instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
public function __construct(Filesystem $files)
{
parent::__construct();

$this->files = $files;
}

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$key = $this->option('key') ?: Env::get('LARAVEL_ENV_ENCRYPTION_KEY');
jbrooksuk marked this conversation as resolved.
Show resolved Hide resolved

if (! $key) {
$this->components->error('A decryption key is required.');

return Command::FAILURE;
}

$cipher = $this->option('cipher') ?: 'AES-256-CBC';

$key = $this->parseKey($key);

$environmentFile = $this->option('env')
? base_path('.env').'.'.$this->option('env')
: $this->laravel->environmentFilePath();

$encryptedFile = $environmentFile.'.encrypted';

$filename = $this->option('filename')
? base_path($this->option('filename'))
: $environmentFile;

if (Str::endsWith($filename, '.encrypted')) {
$this->components->error('Invalid filename.');

return Command::FAILURE;
}

if (! $this->files->exists($encryptedFile)) {
$this->components->error('Encrypted environment file not found.');

return Command::FAILURE;
}

if ($this->files->exists($environmentFile) && ! $this->option('force')) {
$this->components->error('Environment file already exists.');

return Command::FAILURE;
}

try {
$encrypter = new Encrypter($key, $cipher);

$this->files->put(
$filename,
$encrypter->decrypt($this->files->get($encryptedFile))
);
} catch (Exception $e) {
$this->components->error($e->getMessage());

return Command::FAILURE;
}

$this->components->info('Environment successfully decrypted.');

$this->components->twoColumnDetail('Decrypted file', $filename);

$this->newLine();
}

/**
* Parse the encryption key.
*
* @param string $key
* @return string
*/
protected function parseKey(string $key)
{
if (Str::startsWith($key, $prefix = 'base64:')) {
$key = base64_decode(Str::after($key, $prefix));
}

return $key;
}
}
119 changes: 119 additions & 0 deletions src/Illuminate/Foundation/Console/EnvironmentEncryptCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

namespace Illuminate\Foundation\Console;

use Exception;
use Illuminate\Console\Command;
use Illuminate\Encryption\Encrypter;
use Illuminate\Filesystem\Filesystem;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'env:encrypt')]
class EnvironmentEncryptCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'env:encrypt
{--key= : The encryption key}
{--cipher= : The encryption cipher}
{--env= : The environment to be encrypted}
{--force : Overwrite the existing encrypted environment file}';

/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'env:encrypt';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Encrypt an environment file';

/**
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;

/**
* Create a new command instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
public function __construct(Filesystem $files)
{
parent::__construct();

$this->files = $files;
}

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$cipher = $this->option('cipher') ?: 'AES-256-CBC';

$key = $this->option('key');

$keyPassed = $key !== null;

$environmentFile = $this->option('env')
? base_path('.env').'.'.$this->option('env')
: $this->laravel->environmentFilePath();

$encryptedFile = $environmentFile.'.encrypted';

if (! $keyPassed) {
$key = Encrypter::generateKey($cipher);
}

if (! $this->files->exists($environmentFile)) {
$this->components->error('Environment file not found.');

return Command::FAILURE;
}

if ($this->files->exists($encryptedFile) && ! $this->option('force')) {
$this->components->error('Encrypted environment file already exists.');

return Command::FAILURE;
}

try {
$encrypter = new Encrypter($key, $cipher);

$this->files->put(
$encryptedFile,
$encrypter->encrypt($this->files->get($environmentFile))
);
} catch (Exception $e) {
$this->components->error($e->getMessage());

return Command::FAILURE;
}

$this->components->info('Environment successfully encrypted.');

$this->components->twoColumnDetail('Key', ($keyPassed ? $key : 'base64:'.base64_encode($key)));
$this->components->twoColumnDetail('Cipher', $cipher);
$this->components->twoColumnDetail('Encrypted file', $encryptedFile);

$this->newLine();
}
}
24 changes: 24 additions & 0 deletions src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
use Illuminate\Foundation\Console\DocsCommand;
use Illuminate\Foundation\Console\DownCommand;
use Illuminate\Foundation\Console\EnvironmentCommand;
use Illuminate\Foundation\Console\EnvironmentDecryptCommand;
use Illuminate\Foundation\Console\EnvironmentEncryptCommand;
use Illuminate\Foundation\Console\EventCacheCommand;
use Illuminate\Foundation\Console\EventClearCommand;
use Illuminate\Foundation\Console\EventGenerateCommand;
Expand Down Expand Up @@ -112,6 +114,8 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid
'DbWipe' => WipeCommand::class,
'Down' => DownCommand::class,
'Environment' => EnvironmentCommand::class,
'EnvironmentDecrypt' => EnvironmentDecryptCommand::class,
'EnvironmentEncrypt' => EnvironmentEncryptCommand::class,
'EventCache' => EventCacheCommand::class,
'EventClear' => EventClearCommand::class,
'EventList' => EventListCommand::class,
Expand Down Expand Up @@ -507,6 +511,26 @@ protected function registerEnvironmentCommand()
$this->app->singleton(EnvironmentCommand::class);
}

/**
* Register the command.
*
* @return void
*/
protected function registerEnvironmentDecryptCommand()
{
$this->app->singleton(EnvironmentDecryptCommand::class);
}

/**
* Register the command.
*
* @return void
*/
protected function registerEnvironmentEncryptCommand()
{
$this->app->singleton(EnvironmentEncryptCommand::class);
}

/**
* Register the command.
*
Expand Down
Loading