diff --git a/src/Secrets.php b/src/Secrets.php index 639e815..de6a349 100644 --- a/src/Secrets.php +++ b/src/Secrets.php @@ -42,8 +42,9 @@ public static function loadSecretEnvironmentVariables(?SsmClient $ssmClient = nu return self::retrieveParametersFromSsm($ssmClient, array_values($ssmNames)); }); - foreach ($parameters as $parameterName => $parameterValue) { - $envVar = array_search($parameterName, $ssmNames, true); + foreach ($envVarsToDecrypt as $envVar => $prefixedSsmRefName) { + $parameterName = substr($prefixedSsmRefName, strlen('bref-ssm:')); + $parameterValue = $parameters[$parameterName]; $_SERVER[$envVar] = $_ENV[$envVar] = $parameterValue; putenv("$envVar=$parameterValue"); } diff --git a/tests/SecretsTest.php b/tests/SecretsTest.php index 6792ace..9c85a10 100644 --- a/tests/SecretsTest.php +++ b/tests/SecretsTest.php @@ -11,6 +11,11 @@ class SecretsTest extends TestCase { + private array $resultParams = [ + 'Name' => '/some/parameter', + 'Value' => 'foobar', + ]; + public function setUp(): void { if (file_exists(sys_get_temp_dir() . '/bref-ssm-parameters.php')) { @@ -27,13 +32,18 @@ public function test decrypts env variables(): void $this->assertSame('bref-ssm:/some/parameter', getenv('SOME_VARIABLE')); $this->assertSame('helloworld', getenv('SOME_OTHER_VARIABLE')); - Secrets::loadSecretEnvironmentVariables($this->mockSsmClient()); + $ssmClient = $this->mockSsmClient([new Parameter($this->resultParams)]); + Secrets::loadSecretEnvironmentVariables($ssmClient); $this->assertSame('foobar', getenv('SOME_VARIABLE')); $this->assertSame('foobar', $_SERVER['SOME_VARIABLE']); $this->assertSame('foobar', $_ENV['SOME_VARIABLE']); // Check that the other variable was not modified $this->assertSame('helloworld', getenv('SOME_OTHER_VARIABLE')); + + // Cleanup + putenv('SOME_VARIABLE'); + putenv('SOME_OTHER_VARIABLE'); } public function test caches parameters to call SSM only once(): void @@ -41,11 +51,37 @@ public function test caches parameters to call SSM only once(): void putenv('SOME_VARIABLE=bref-ssm:/some/parameter'); // Call twice, the mock will assert that SSM was only called once - $ssmClient = $this->mockSsmClient(); + $ssmClient = $this->mockSsmClient([new Parameter($this->resultParams)]); Secrets::loadSecretEnvironmentVariables($ssmClient); Secrets::loadSecretEnvironmentVariables($ssmClient); $this->assertSame('foobar', getenv('SOME_VARIABLE')); + + // Cleanup + putenv('SOME_VARIABLE'); + } + + public function test same ssm value can be assigned more than once(): void + { + putenv('VAR1=bref-ssm:/some/parameter'); + putenv('VAR2=bref-ssm:/some/parameter'); + + // Sanity checks + $this->assertSame('bref-ssm:/some/parameter', getenv('VAR1')); + $this->assertSame('bref-ssm:/some/parameter', getenv('VAR2')); + + $ssmClient = $this->mockSsmClient([ + new Parameter($this->resultParams), + new Parameter($this->resultParams), + ]); + Secrets::loadSecretEnvironmentVariables($ssmClient); + + $this->assertSame('foobar', getenv('VAR1')); + $this->assertSame('foobar', getenv('VAR2')); + + // Cleanup + putenv('VAR1'); + putenv('VAR2'); } public function test throws a clear error message on missing permissions(): void @@ -62,9 +98,15 @@ public function test throws a clear error message on missing permissions $expected = preg_quote("Bref was not able to resolve secrets contained in environment variables from SSM because of a permissions issue with the SSM API. Did you add IAM permissions in serverless.yml to allow Lambda to access SSM? (docs: https://bref.sh/docs/environment/variables.html#at-deployment-time).\nFull exception message:", '/'); $this->expectExceptionMessageMatches("/$expected .+/"); Secrets::loadSecretEnvironmentVariables($ssmClient); + + // Cleanup + putenv('SOME_VARIABLE'); } - private function mockSsmClient(): SsmClient + /** + * @param array $resultParameters + */ + private function mockSsmClient(array $resultParameters): SsmClient { $ssmClient = $this->getMockBuilder(SsmClient::class) ->disableOriginalConstructor() @@ -72,18 +114,17 @@ private function mockSsmClient(): SsmClient ->getMock(); $result = ResultMockFactory::create(GetParametersResult::class, [ - 'Parameters' => [ - new Parameter([ - 'Name' => '/some/parameter', - 'Value' => 'foobar', - ]), - ], + 'Parameters' => $resultParameters, ]); + $expectedNames = []; + foreach ($resultParameters as $resultParameter) { + $expectedNames[] = $resultParameter->getName(); + } $ssmClient->expects($this->once()) ->method('getParameters') ->with([ - 'Names' => ['/some/parameter'], + 'Names' => $expectedNames, 'WithDecryption' => true, ]) ->willReturn($result);