PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Bundle/FrameworkBundle/Command/SecretsSetCommand.php

https://github.com/FabienD/symfony
PHP | 147 lines | 102 code | 29 blank | 16 comment | 13 complexity | 62c4e5749380fb789f41478b9753f55f MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Bundle\FrameworkBundle\Command;
  11. use Symfony\Bundle\FrameworkBundle\Secrets\AbstractVault;
  12. use Symfony\Component\Console\Attribute\AsCommand;
  13. use Symfony\Component\Console\Command\Command;
  14. use Symfony\Component\Console\Completion\CompletionInput;
  15. use Symfony\Component\Console\Completion\CompletionSuggestions;
  16. use Symfony\Component\Console\Input\InputArgument;
  17. use Symfony\Component\Console\Input\InputInterface;
  18. use Symfony\Component\Console\Input\InputOption;
  19. use Symfony\Component\Console\Output\ConsoleOutputInterface;
  20. use Symfony\Component\Console\Output\OutputInterface;
  21. use Symfony\Component\Console\Style\SymfonyStyle;
  22. /**
  23. * @author Tobias Schultze <http://tobion.de>
  24. * @author Jérémy Derussé <jeremy@derusse.com>
  25. * @author Nicolas Grekas <p@tchwork.com>
  26. *
  27. * @internal
  28. */
  29. #[AsCommand(name: 'secrets:set', description: 'Set a secret in the vault')]
  30. final class SecretsSetCommand extends Command
  31. {
  32. private AbstractVault $vault;
  33. private ?AbstractVault $localVault;
  34. public function __construct(AbstractVault $vault, AbstractVault $localVault = null)
  35. {
  36. $this->vault = $vault;
  37. $this->localVault = $localVault;
  38. parent::__construct();
  39. }
  40. protected function configure()
  41. {
  42. $this
  43. ->addArgument('name', InputArgument::REQUIRED, 'The name of the secret')
  44. ->addArgument('file', InputArgument::OPTIONAL, 'A file where to read the secret from or "-" for reading from STDIN')
  45. ->addOption('local', 'l', InputOption::VALUE_NONE, 'Update the local vault.')
  46. ->addOption('random', 'r', InputOption::VALUE_OPTIONAL, 'Generate a random value.', false)
  47. ->setHelp(<<<'EOF'
  48. The <info>%command.name%</info> command stores a secret in the vault.
  49. <info>%command.full_name% <name></info>
  50. To reference secrets in services.yaml or any other config
  51. files, use <info>"%env(<name>)%"</info>.
  52. By default, the secret value should be entered interactively.
  53. Alternatively, provide a file where to read the secret from:
  54. <info>php %command.full_name% <name> filename</info>
  55. Use "-" as a file name to read from STDIN:
  56. <info>cat filename | php %command.full_name% <name> -</info>
  57. Use <info>--local</info> to override secrets for local needs.
  58. EOF
  59. )
  60. ;
  61. }
  62. protected function execute(InputInterface $input, OutputInterface $output): int
  63. {
  64. $errOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output;
  65. $io = new SymfonyStyle($input, $errOutput);
  66. $name = $input->getArgument('name');
  67. $vault = $input->getOption('local') ? $this->localVault : $this->vault;
  68. if (null === $vault) {
  69. $io->error('The local vault is disabled.');
  70. return 1;
  71. }
  72. if ($this->localVault === $vault && !\array_key_exists($name, $this->vault->list())) {
  73. $io->error(sprintf('Secret "%s" does not exist in the vault, you cannot override it locally.', $name));
  74. return 1;
  75. }
  76. if (0 < $random = $input->getOption('random') ?? 16) {
  77. $value = strtr(substr(base64_encode(random_bytes($random)), 0, $random), '+/', '-_');
  78. } elseif (!$file = $input->getArgument('file')) {
  79. $value = $io->askHidden('Please type the secret value');
  80. if (null === $value) {
  81. $io->warning('No value provided: using empty string');
  82. $value = '';
  83. }
  84. } elseif ('-' === $file) {
  85. $value = file_get_contents('php://stdin');
  86. } elseif (is_file($file) && is_readable($file)) {
  87. $value = file_get_contents($file);
  88. } elseif (!is_file($file)) {
  89. throw new \InvalidArgumentException(sprintf('File not found: "%s".', $file));
  90. } elseif (!is_readable($file)) {
  91. throw new \InvalidArgumentException(sprintf('File is not readable: "%s".', $file));
  92. }
  93. if ($vault->generateKeys()) {
  94. $io->success($vault->getLastMessage());
  95. if ($this->vault === $vault) {
  96. $io->caution('DO NOT COMMIT THE DECRYPTION KEY FOR THE PROD ENVIRONMENT⚠️');
  97. }
  98. }
  99. $vault->seal($name, $value);
  100. $io->success($vault->getLastMessage() ?? 'Secret was successfully stored in the vault.');
  101. if (0 < $random) {
  102. $errOutput->write(' // The generated random value is: <comment>');
  103. $output->write($value);
  104. $errOutput->writeln('</comment>');
  105. $io->newLine();
  106. }
  107. if ($this->vault === $vault && null !== $this->localVault->reveal($name)) {
  108. $io->comment('Note that this secret is overridden in the local vault.');
  109. }
  110. return 0;
  111. }
  112. public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
  113. {
  114. if ($input->mustSuggestArgumentValuesFor('name')) {
  115. $suggestions->suggestValues(array_keys($this->vault->list(false)));
  116. }
  117. }
  118. }