public function testDecrypt() { $encrypted_key = 'hu8gZ19fINO3HrPUQkHgqdZ5qaLcKFg4kRgUISFOhQWiAWVYbyo53gE6JQ61gZ64tvclc0RtVghPZCGLvQSJvmQxFwdxzXF7mqvJdepFXhIdwJ6PMIE701kGmdjNoSoXFiOACb73c70DZQmzLA4sVulwcTtJcMWsRgpiC4PayrNWun9x0bWsvsDNNZ2YPZL8yf0paeyfLPsMONfqakSUrJfprYFtjbEkWcgx0hluvZpHj3Nl6AU3TFBJS6b8qFrp99qVsS7wBKAUNSUrDBzGjYNy'; $decrypted_key = 'FOGdGgtmjWZufLyyCe91QG1njTKrylvn8wv0RaLmoCd7sEhDKAjJ6iHGXJPRrfdm2Mtw2nlpBjbukKBU'; $credentials_param = Parameter::make(['id' => 'cloud_credentials', 'sensitive' => true, 'value' => ['access_key' => 'test-key', 'secret_key' => $encrypted_key]]); $params = ParametersCollection::make(['StackName' => 'test-stack', 'cloud_credentials' => $credentials_param]); $unencrypted_values = ['test-stack', 'cloud_credentials', true, 'test-key']; foreach ($unencrypted_values as $value) { Crypt::shouldReceive('decrypt')->once()->with($value)->andThrow(new \Illuminate\Contracts\Encryption\DecryptException('Invalid data.')); } Crypt::shouldReceive('decrypt')->once()->with($encrypted_key)->andReturn($decrypted_key); $decrypted_params = $params->decrypted()->toArray(); $this->assertEquals(array_get($decrypted_params, 'cloud_credentials.value.secret_key'), $decrypted_key); }
/** * @param Stack $stack * @param $action * @return Stack * @throws \Aws\CloudFormation\Exception\CloudFormationException * @throws \Exception */ private function manageStack(Stack $stack, $action) { $cfn_params = new ParametersCollection(['StackName' => $stack->get('name'), 'DisableRollback' => true, 'Capabilities' => ['CAPABILITY_IAM']]); switch ($action) { case 'create': case 'update': $stack_params = $this->getParams($stack, $with_outputs = true); $stack_params->forget('cloud_credentials'); $template = $this->generateTemplate($stack, $stack_params); $bucket = $stack_params->get(Stack::PARAM_TEMPLATE_BUCKET)->value; $s3_template = $this->publishTemplate($stack, $bucket, $template); $cfn_params->put('TemplateURL', $s3_template['url']); $params = $cfn_params->merge($stack_params); $params->forget('components'); $defined_params = TemplateEngine::getParams($stack->template); $defined_params_keys = ParametersCollection::make($defined_params)->parametersKeys(); $sensitive = $params->onlyIds($defined_params_keys)->sensitive()->flat()->studly()->decrypted()->toCollection(); if ($sensitive_aws = $sensitive->toAws()) { $cfn_params->put('Parameters', $sensitive_aws); } // TODO protect credentials from logging / output try { $action == 'create' ? $this->cfn($stack)->createStack($cfn_params->toArray()) : $this->cfn($stack)->updateStack($cfn_params->toArray()); } catch (CloudFormationException $e) { if ($e->getMessage() != 'No updates are to be performed.') { throw $e; } } finally { $this->destroyTemplate($stack, $bucket, $s3_template['key']); } break; case 'describe': Log::debug(__METHOD__); if ($stack->provisioned) { $components = ComponentCollection::make($stack->components->all()); $cache_key = 'CloudFormer-DescribeStack-' . $stack->name; if (($cf_response = Cache::get($cache_key)) === null) { Log::debug('No cache for ' . $cache_key); $cf_response = $this->cfn($stack)->describeStacks($cfn_params->toArray()); Cache::add($cache_key, $cf_response, self::CACHE_LIFETIME); } $cf_response = head($cf_response->get('Stacks')); $stack->status = $this->translateAwsStatus($cf_response['StackStatus']); $stack->outputs = Collection::make([]); $distributedOutputs = $components->distributeResources(data_get($cf_response, 'Outputs', []), 'OutputKey'); $distributedOutputs->map(function (Collection $componentOutputs, $componentName) use($components, $stack) { if (in_array($componentName, ['_stack', '_ungrouped'])) { $stack->outputs = $stack->outputs->merge($componentOutputs); } else { $components[$componentName]->outputs = $componentOutputs; } }); $cache_key = 'CloudFormer-ListStackResources-' . $stack->name; if (($cf_stack_resources = Cache::get($cache_key)) === null) { Log::debug('No cache for ' . $cache_key); $cf_stack_resources = $this->cfn($stack)->listStackResources($cfn_params->toArray())['StackResourceSummaries']; Cache::add($cache_key, $cf_stack_resources, self::CACHE_LIFETIME); } $components->distributeResources($cf_stack_resources)->map(function (Collection $resources, $componentName) use($components, $stack) { $status = $resources->reduce(function ($carry, $item) { $itemStatus = $this->translateAwsStatus($item['ResourceStatus']); if ($carry === null) { return $itemStatus; } return $this->weighStatus($itemStatus) > $this->weighStatus($carry) ? $itemStatus : $carry; }); if (!in_array($componentName, ['_stack', '_ungrouped']) && is_object($status)) { $components[$componentName]->status = $status; } }); } break; case 'delete': $this->cfn($stack)->deleteStack($cfn_params->toArray()); break; } return $stack; }