Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
270
src/Framework/Config/Console/SecretsCommand.php
Normal file
270
src/Framework/Config/Console/SecretsCommand.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Config\Console;
|
||||
|
||||
use App\Framework\Config\EncryptedEnvLoader;
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Config\SecretManager;
|
||||
use App\Framework\Console\ConsoleColor;
|
||||
use App\Framework\Console\ConsoleCommand;
|
||||
use App\Framework\Console\ConsoleInput;
|
||||
use App\Framework\Console\ConsoleOutput;
|
||||
use App\Framework\Console\ExitCode;
|
||||
use App\Framework\Encryption\EncryptionFactory;
|
||||
use App\Framework\Random\RandomGenerator;
|
||||
|
||||
/**
|
||||
* Console commands for secrets management
|
||||
*/
|
||||
final readonly class SecretsCommand
|
||||
{
|
||||
public function __construct(
|
||||
private RandomGenerator $randomGenerator,
|
||||
private EncryptionFactory $encryptionFactory,
|
||||
private ?SecretManager $secretManager = null
|
||||
) {
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:generate-key', 'Generate a secure encryption key')]
|
||||
public function generateKey(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
$output->writeLine('🔐 Generating encryption key...', ConsoleColor::CYAN);
|
||||
$output->newLine();
|
||||
|
||||
try {
|
||||
$length = 32;
|
||||
$keyBytes = $this->randomGenerator->bytes($length);
|
||||
$encodedKey = base64_encode($keyBytes);
|
||||
|
||||
$availableMethods = $this->encryptionFactory->getAvailableMethods();
|
||||
$recommendedMethod = $this->encryptionFactory->getRecommendedMethod();
|
||||
|
||||
$output->writeLine("Available methods: " . implode(', ', $availableMethods));
|
||||
$output->writeLine("Using recommended method: {$recommendedMethod}", ConsoleColor::GREEN);
|
||||
$output->newLine();
|
||||
|
||||
$output->writeSuccess('✅ Encryption key generated successfully!');
|
||||
$output->newLine();
|
||||
$output->writeLine('ENCRYPTION_KEY=' . $encodedKey, ConsoleColor::YELLOW);
|
||||
$output->newLine();
|
||||
|
||||
$output->writeWarning('🚨 SECURITY WARNINGS:');
|
||||
$output->writeLine('• Store this key securely - it cannot be recovered if lost');
|
||||
$output->writeLine('• Add this to your .env file (never commit to version control)');
|
||||
$output->writeLine('• Use different keys for different environments');
|
||||
$output->newLine();
|
||||
|
||||
$output->writeInfo('📋 Next steps:');
|
||||
$output->writeLine('1. Add the key to your .env file');
|
||||
$output->writeLine('2. Create .env.secrets file: php console.php secrets:init');
|
||||
$output->writeLine('3. Encrypt secrets: php console.php secrets:encrypt "secret-value"');
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeError('❌ Failed to generate encryption key: ' . $e->getMessage());
|
||||
|
||||
return ExitCode::GENERAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:encrypt', 'Encrypt a secret value')]
|
||||
public function encrypt(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
if ($this->secretManager === null) {
|
||||
$output->writeError('❌ SecretManager not available. Make sure ENCRYPTION_KEY is set in .env');
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
|
||||
$value = $input->getArgument(0);
|
||||
if (! $value) {
|
||||
$value = $output->askPassword('Enter secret value to encrypt:');
|
||||
}
|
||||
|
||||
if (empty($value)) {
|
||||
$output->writeError('❌ No value provided to encrypt');
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
|
||||
try {
|
||||
$encrypted = $this->secretManager->encryptSecret($value);
|
||||
|
||||
$output->writeSuccess('✅ Value encrypted successfully!');
|
||||
$output->newLine();
|
||||
$output->writeLine('Encrypted value:', ConsoleColor::CYAN);
|
||||
$output->writeLine($encrypted, ConsoleColor::YELLOW);
|
||||
$output->newLine();
|
||||
$output->writeInfo('💡 Add this to your .env.secrets file like:');
|
||||
$output->writeLine('SECRET_YOUR_KEY=' . $encrypted, ConsoleColor::GRAY);
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeError('❌ Failed to encrypt value: ' . $e->getMessage());
|
||||
|
||||
return ExitCode::SOFTWARE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:decrypt', 'Decrypt a secret value (for debugging)')]
|
||||
public function decrypt(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
if ($this->secretManager === null) {
|
||||
$output->writeError('❌ SecretManager not available. Make sure ENCRYPTION_KEY is set in .env');
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
|
||||
$encryptedValue = $input->getArgument(0);
|
||||
if (! $encryptedValue) {
|
||||
$encryptedValue = $output->askQuestion('Enter encrypted value to decrypt:');
|
||||
}
|
||||
|
||||
if (empty($encryptedValue)) {
|
||||
$output->writeError('❌ No encrypted value provided');
|
||||
|
||||
return ExitCode::INVALID_INPUT;
|
||||
}
|
||||
|
||||
try {
|
||||
if (! $this->secretManager->isEncrypted($encryptedValue)) {
|
||||
$output->writeWarning('⚠️ Value does not appear to be encrypted');
|
||||
|
||||
return ExitCode::INVALID_INPUT;
|
||||
}
|
||||
|
||||
$decrypted = $this->secretManager->getSecret('TEMP_DECRYPT', $encryptedValue);
|
||||
|
||||
$output->writeSuccess('✅ Value decrypted successfully!');
|
||||
$output->newLine();
|
||||
$output->writeLine('Decrypted value:', ConsoleColor::CYAN);
|
||||
$output->writeLine($decrypted, ConsoleColor::YELLOW);
|
||||
$output->newLine();
|
||||
$output->writeWarning('🔒 This value should be kept secure!');
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeError('❌ Failed to decrypt value: ' . $e->getMessage());
|
||||
|
||||
return ExitCode::SOFTWARE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:init', 'Initialize .env.secrets file')]
|
||||
public function init(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
$basePath = getcwd();
|
||||
$secretsFile = $basePath . '/.env.secrets';
|
||||
|
||||
if (file_exists($secretsFile)) {
|
||||
if (! $output->confirm('⚠️ .env.secrets already exists. Overwrite?', false)) {
|
||||
$output->writeInfo('Operation cancelled');
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$encryptedLoader = new EncryptedEnvLoader($this->encryptionFactory, $this->randomGenerator);
|
||||
$filePath = $encryptedLoader->generateSecretsTemplate($basePath);
|
||||
|
||||
$output->writeSuccess('✅ .env.secrets template created!');
|
||||
$output->writeLine("File created at: {$filePath}", ConsoleColor::GRAY);
|
||||
$output->newLine();
|
||||
$output->writeInfo('📋 Next steps:');
|
||||
$output->writeLine('1. Edit .env.secrets and add your secret keys');
|
||||
$output->writeLine('2. Encrypt values: php console.php secrets:encrypt "your-value"');
|
||||
$output->writeLine('3. Add encrypted values to .env.secrets');
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeError('❌ Failed to create .env.secrets template: ' . $e->getMessage());
|
||||
|
||||
return ExitCode::CANT_CREATE;
|
||||
}
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:list', 'List all secret keys')]
|
||||
public function list(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
if ($this->secretManager === null) {
|
||||
$output->writeError('❌ SecretManager not available. Make sure ENCRYPTION_KEY is set in .env');
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
|
||||
try {
|
||||
$secretKeys = $this->secretManager->getSecretKeys();
|
||||
|
||||
if (empty($secretKeys)) {
|
||||
$output->writeInfo('ℹ️ No secret keys found');
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
$output->writeLine('🔐 Found secret keys:', ConsoleColor::CYAN);
|
||||
$output->newLine();
|
||||
|
||||
foreach ($secretKeys as $key) {
|
||||
$output->writeLine("• {$key}", ConsoleColor::WHITE);
|
||||
}
|
||||
|
||||
$output->newLine();
|
||||
$output->writeLine("Total: " . count($secretKeys) . " secrets", ConsoleColor::GRAY);
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
} catch (\Throwable $e) {
|
||||
$output->writeError('❌ Failed to list secrets: ' . $e->getMessage());
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#[ConsoleCommand('secrets:validate', 'Validate secrets setup')]
|
||||
public function validate(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
$basePath = getcwd();
|
||||
$encryptedLoader = new EncryptedEnvLoader($this->encryptionFactory, $this->randomGenerator);
|
||||
$env = Environment::fromFile($basePath . '/.env');
|
||||
$encryptionKey = $env->get('ENCRYPTION_KEY');
|
||||
|
||||
$output->writeLine('🔍 Validating secrets setup...', ConsoleColor::CYAN);
|
||||
$output->newLine();
|
||||
|
||||
$issues = $encryptedLoader->validateEncryptionSetup($basePath, $encryptionKey);
|
||||
|
||||
if (empty($issues)) {
|
||||
$output->writeSuccess('✅ Secrets setup is valid!');
|
||||
|
||||
if ($this->secretManager !== null) {
|
||||
$context = $this->secretManager->getSecurityContext();
|
||||
$output->newLine();
|
||||
$output->writeInfo('Security context:');
|
||||
$output->writeLine("• HTTPS: " . ($context->isHttps ? 'Yes' : 'No'));
|
||||
$output->writeLine("• Encryption: {$context->encryptionMethod}");
|
||||
$output->writeLine("• Server: {$context->serverName}");
|
||||
$output->writeLine("• Risk Level: {$context->getRiskLevel()->getDisplayName()}");
|
||||
$output->writeLine("• Summary: {$context->getSummary()}");
|
||||
}
|
||||
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
$output->writeWarning('⚠️ Found issues with secrets setup:');
|
||||
$output->newLine();
|
||||
|
||||
foreach ($issues as $issue) {
|
||||
$color = match ($issue['severity']) {
|
||||
'high' => ConsoleColor::RED,
|
||||
'medium' => ConsoleColor::YELLOW,
|
||||
default => ConsoleColor::WHITE
|
||||
};
|
||||
|
||||
$output->writeLine("• {$issue['message']}", $color);
|
||||
}
|
||||
|
||||
return ExitCode::CONFIG_ERROR;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user