Skip to content

Commit 776875d

Browse files
danielburger1337fabpot
authored andcommitted
Add SecretsRevealCommand
1 parent 6ed40f0 commit 776875d

File tree

5 files changed

+168
-0
lines changed

5 files changed

+168
-0
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* Move the Router `cache_dir` to `kernel.build_dir`
1010
* Deprecate the `router.cache_dir` config option
1111
* Add `rate_limiter` tags to rate limiter services
12+
* Add `secrets:reveal` command
1213

1314
7.0
1415
---
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
15+
use Symfony\Component\Console\Attribute\AsCommand;
16+
use Symfony\Component\Console\Command\Command;
17+
use Symfony\Component\Console\Input\InputArgument;
18+
use Symfony\Component\Console\Input\InputInterface;
19+
use Symfony\Component\Console\Output\ConsoleOutputInterface;
20+
use Symfony\Component\Console\Output\OutputInterface;
21+
use Symfony\Component\Console\Style\SymfonyStyle;
22+
23+
/**
24+
* @internal
25+
*/
26+
#[AsCommand(name: 'secrets:reveal', description: 'Reveal the value of a secret')]
27+
final class SecretsRevealCommand extends Command
28+
{
29+
public function __construct(
30+
private readonly AbstractVault $vault,
31+
private readonly ?AbstractVault $localVault = null,
32+
) {
33+
parent::__construct();
34+
}
35+
36+
protected function configure(): void
37+
{
38+
$this
39+
->addArgument('name', InputArgument::REQUIRED, 'The name of the secret to reveal', null, fn () => array_keys($this->vault->list()))
40+
->setHelp(<<<'EOF'
41+
The <info>%command.name%</info> command reveals a stored secret.
42+
43+
<info>%command.full_name%</info>
44+
EOF
45+
)
46+
;
47+
}
48+
49+
protected function execute(InputInterface $input, OutputInterface $output): int
50+
{
51+
$io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output);
52+
53+
$secrets = $this->vault->list(true);
54+
$localSecrets = $this->localVault?->list(true);
55+
56+
$name = (string) $input->getArgument('name');
57+
58+
if (null !== $localSecrets && \array_key_exists($name, $localSecrets)) {
59+
$io->writeln($localSecrets[$name]);
60+
} else {
61+
if (!\array_key_exists($name, $secrets)) {
62+
$io->error(sprintf('The secret "%s" does not exist.', $name));
63+
64+
return self::INVALID;
65+
}
66+
67+
$io->writeln($secrets[$name]);
68+
}
69+
70+
return self::SUCCESS;
71+
}
72+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c
17731773
if (!$this->readConfigEnabled('secrets', $container, $config)) {
17741774
$container->removeDefinition('console.command.secrets_set');
17751775
$container->removeDefinition('console.command.secrets_list');
1776+
$container->removeDefinition('console.command.secrets_reveal');
17761777
$container->removeDefinition('console.command.secrets_remove');
17771778
$container->removeDefinition('console.command.secrets_generate_key');
17781779
$container->removeDefinition('console.command.secrets_decrypt_to_local');

src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Symfony\Bundle\FrameworkBundle\Command\SecretsGenerateKeysCommand;
3434
use Symfony\Bundle\FrameworkBundle\Command\SecretsListCommand;
3535
use Symfony\Bundle\FrameworkBundle\Command\SecretsRemoveCommand;
36+
use Symfony\Bundle\FrameworkBundle\Command\SecretsRevealCommand;
3637
use Symfony\Bundle\FrameworkBundle\Command\SecretsSetCommand;
3738
use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand;
3839
use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand;
@@ -355,6 +356,13 @@
355356
])
356357
->tag('console.command')
357358

359+
->set('console.command.secrets_reveal', SecretsRevealCommand::class)
360+
->args([
361+
service('secrets.vault'),
362+
service('secrets.local_vault')->ignoreOnInvalid(),
363+
])
364+
->tag('console.command')
365+
358366
->set('console.command.secrets_decrypt_to_local', SecretsDecryptToLocalCommand::class)
359367
->args([
360368
service('secrets.vault'),
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Tests\Command;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bundle\FrameworkBundle\Command\SecretsRevealCommand;
16+
use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
17+
use Symfony\Bundle\FrameworkBundle\Secrets\DotenvVault;
18+
use Symfony\Component\Console\Command\Command;
19+
use Symfony\Component\Console\Tester\CommandTester;
20+
21+
class SecretsRevealCommandTest extends TestCase
22+
{
23+
public function testExecute()
24+
{
25+
$vault = $this->createMock(AbstractVault::class);
26+
$vault->method('list')->willReturn(['secretKey' => 'secretValue']);
27+
28+
$command = new SecretsRevealCommand($vault);
29+
30+
$tester = new CommandTester($command);
31+
$this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey']));
32+
33+
$this->assertEquals('secretValue', trim($tester->getDisplay(true)));
34+
}
35+
36+
public function testInvalidName()
37+
{
38+
$vault = $this->createMock(AbstractVault::class);
39+
$vault->method('list')->willReturn(['secretKey' => 'secretValue']);
40+
41+
$command = new SecretsRevealCommand($vault);
42+
43+
$tester = new CommandTester($command);
44+
$this->assertSame(Command::INVALID, $tester->execute(['name' => 'undefinedKey']));
45+
46+
$this->assertStringContainsString('The secret "undefinedKey" does not exist.', trim($tester->getDisplay(true)));
47+
}
48+
49+
/**
50+
* @backupGlobals enabled
51+
*/
52+
public function testLocalVaultOverride()
53+
{
54+
$vault = $this->createMock(AbstractVault::class);
55+
$vault->method('list')->willReturn(['secretKey' => 'secretValue']);
56+
57+
$_ENV = ['secretKey' => 'newSecretValue'];
58+
$localVault = new DotenvVault('/not/a/path');
59+
60+
$command = new SecretsRevealCommand($vault, $localVault);
61+
62+
$tester = new CommandTester($command);
63+
$this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey']));
64+
65+
$this->assertEquals('newSecretValue', trim($tester->getDisplay(true)));
66+
}
67+
68+
/**
69+
* @backupGlobals enabled
70+
*/
71+
public function testOnlyLocalVaultContainsName()
72+
{
73+
$vault = $this->createMock(AbstractVault::class);
74+
$vault->method('list')->willReturn(['otherKey' => 'secretValue']);
75+
76+
$_ENV = ['secretKey' => 'secretValue'];
77+
$localVault = new DotenvVault('/not/a/path');
78+
79+
$command = new SecretsRevealCommand($vault, $localVault);
80+
81+
$tester = new CommandTester($command);
82+
$this->assertSame(Command::SUCCESS, $tester->execute(['name' => 'secretKey']));
83+
84+
$this->assertEquals('secretValue', trim($tester->getDisplay(true)));
85+
}
86+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy