From f6b4334c59a1ac0bfb8b877baccb02b894ef24e4 Mon Sep 17 00:00:00 2001 From: Nick Lynch Date: Mon, 30 Nov 2020 09:48:26 +0000 Subject: [PATCH 1/6] fix(secretsmanager): secretName for owned secrets includes suffix (under feature flag) (#11736) Proper (tokenized) secret names for owned secrets was introduced in #11202. This caused `Secret.secretName` to return the full resource name (secret name + suffix) for owned secrets. This was done to ensure that the secretName was a Tokenized value that would create a dependency between the Secret and its consumer. However, Secrets Manager's DescribeSecret API does not accept the resourceName as an input; it accepts the full ARN, a partial ARN (without the suffix), or the secret name (without the suffix). This leads to several integrations with other services or custom scripts to fail when using the secretName from an owned Secret. For owned secrets, we can parse the resourceName to get the secretName based on either (a) knowing the secret name or (b) knowing the format that CloudFormation uses when secret names aren't specified. This fix introduces proper parsing of secret names for owned secrets (behind a feature flag). BREAKING CHANGE: (feature flag) Secret.secretName for owned secrets will now only the secret name (without suffix) and not the full resource name. This is enabled through the `@aws-cdk/secretsmanager:parseOwnedSecretName` flag. fixes #11727 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-secretsmanager/lib/secret.ts | 46 +- .../@aws-cdk/aws-secretsmanager/package.json | 2 + .../integ.secret-name-parsed.expected.json | 392 ++++++++++++++++++ .../.gitignore | 1 + .../integ.secret-name-parsed.handler/index.js | 21 + .../test/integ.secret-name-parsed.ts | 47 +++ .../aws-secretsmanager/test/secret.test.ts | 115 ++++- packages/@aws-cdk/cx-api/lib/features.ts | 13 +- 8 files changed, 619 insertions(+), 18 deletions(-) create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js create mode 100644 packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts diff --git a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts index 33bc8a709efcf..2b5361572e804 100644 --- a/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts +++ b/packages/@aws-cdk/aws-secretsmanager/lib/secret.ts @@ -1,6 +1,7 @@ import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; -import { IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/core'; +import { FeatureFlags, Fn, IResource, RemovalPolicy, Resource, SecretValue, Stack, Token } from '@aws-cdk/core'; +import * as cxapi from '@aws-cdk/cx-api'; import { IConstruct, Construct } from 'constructs'; import { ResourcePolicy } from './policy'; import { RotationSchedule, RotationScheduleOptions } from './rotation-schedule'; @@ -30,7 +31,10 @@ export interface ISecret extends IResource { readonly secretFullArn?: string; /** - * The name of the secret + * The name of the secret. + * + * For "owned" secrets, this will be the full resource name (secret name + suffix), unless the + * '@aws-cdk/aws-secretsmanager:parseOwnedSecretName' feature flag is set. */ readonly secretName: string; @@ -437,7 +441,10 @@ export class Secret extends SecretBase { }); this.encryptionKey = props.encryptionKey; - this.secretName = parseSecretName(this, this.secretArn); + const parseOwnedSecretName = FeatureFlags.of(this).isEnabled(cxapi.SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME); + this.secretName = parseOwnedSecretName + ? parseSecretNameForOwnedSecret(this, this.secretArn, props.secretName) + : parseSecretName(this, this.secretArn); // @see https://docs.aws.amazon.com/kms/latest/developerguide/services-secrets-manager.html#asm-authz const principal = @@ -707,6 +714,39 @@ function parseSecretName(construct: IConstruct, secretArn: string) { throw new Error('invalid ARN format; no secret name provided'); } +/** + * Parses the secret name from the ARN of an owned secret. With owned secrets we know a few things we don't with imported secrets: + * - The ARN is guaranteed to be a full ARN, with suffix. + * - The name -- if provided -- will tell us how many hyphens to expect in the final secret name. + * - If the name is not provided, we know the format used by CloudFormation for auto-generated names. + * + * Note: This is done rather than just returning the secret name passed in by the user to keep the relationship + * explicit between the Secret and wherever the secretName might be used (i.e., using Tokens). + */ +function parseSecretNameForOwnedSecret(construct: Construct, secretArn: string, secretName?: string) { + const resourceName = Stack.of(construct).parseArn(secretArn, ':').resourceName; + if (!resourceName) { + throw new Error('invalid ARN format; no secret name provided'); + } + + // Secret name was explicitly provided, but is unresolved; best option is to use it directly. + // If it came from another Secret, it should (hopefully) already be properly formatted. + if (secretName && Token.isUnresolved(secretName)) { + return secretName; + } + + // If no secretName was provided, the name will be automatically generated by CloudFormation. + // The autogenerated names have the form of `${logicalID}-${random}`. + // Otherwise, we can use the existing secretName to determine how to parse the resulting resourceName. + const secretNameHyphenatedSegments = secretName ? secretName.split('-').length : 2; + // 2 => [0, 1] + const segmentIndexes = [...new Array(secretNameHyphenatedSegments)].map((_, i) => i); + + // Create the secret name from the resource name by joining all the known segments together. + // This should have the effect of stripping the final hyphen and SecretManager suffix. + return Fn.join('-', segmentIndexes.map(i => Fn.select(i, Fn.split('-', resourceName)))); +} + /** Performs a best guess if an ARN is complete, based on if it ends with a 6-character suffix. */ function arnIsComplete(secretArn: string): boolean { return Token.isUnresolved(secretArn) || /-[a-z0-9]{6}$/i.test(secretArn); diff --git a/packages/@aws-cdk/aws-secretsmanager/package.json b/packages/@aws-cdk/aws-secretsmanager/package.json index 9cf9e71ee87e7..9c6c5a435b3a8 100644 --- a/packages/@aws-cdk/aws-secretsmanager/package.json +++ b/packages/@aws-cdk/aws-secretsmanager/package.json @@ -86,6 +86,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.2.0" }, "peerDependencies": { @@ -95,6 +96,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-sam": "0.0.0", "@aws-cdk/core": "0.0.0", + "@aws-cdk/cx-api": "0.0.0", "constructs": "^3.2.0" }, "engines": { diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json new file mode 100644 index 0000000000000..b1b91a239d649 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.expected.json @@ -0,0 +1,392 @@ +{ + "Resources": { + "DefaultSecret67034726": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + } + }, + "NamedSecret7CD5422D": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {}, + "Name": "namedSecret" + } + }, + "NamedSecretWithHyphen6DC9716A": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {}, + "Name": "named-secret-1" + } + }, + "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "GenerateSecretString": {} + } + }, + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ] + }, + "ManagedPolicyArns": [ + { + "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + } + ], + "Policies": [ + { + "PolicyName": "Inline", + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Resource": [ + { + "Ref": "DefaultSecret67034726" + }, + { + "Ref": "NamedSecret7CD5422D" + }, + { + "Ref": "NamedSecretWithHyphen6DC9716A" + }, + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ], + "Action": [ + "secretsmanager:DescribeSecret" + ] + } + ] + } + } + ] + } + }, + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderHandler2A57F6AE": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3BucketED542E1C" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6" + } + ] + } + ] + } + ] + ] + } + }, + "Timeout": 900, + "MemorySize": 128, + "Handler": "__entrypoint__.handler", + "Role": { + "Fn::GetAtt": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A", + "Arn" + ] + }, + "Runtime": "nodejs12.x" + }, + "DependsOn": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderRole4A6F8B2A" + ] + }, + "SecretNameVerification": { + "Type": "Custom::IntegVerificationSecretNameMatches", + "Properties": { + "ServiceToken": { + "Fn::GetAtt": [ + "CustomIntegVerificationSecretNameMatchesCustomResourceProviderHandler2A57F6AE", + "Arn" + ] + }, + "Secrets": [ + { + "secretArn": { + "Ref": "DefaultSecret67034726" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "DefaultSecret67034726" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "DefaultSecret67034726" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + }, + { + "secretArn": { + "Ref": "NamedSecret7CD5422D" + }, + "secretName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecret7CD5422D" + } + ] + } + ] + } + ] + } + ] + } + }, + { + "secretArn": { + "Ref": "NamedSecretWithHyphen6DC9716A" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 2, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "NamedSecretWithHyphen6DC9716A" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + }, + { + "secretArn": { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + }, + "secretName": { + "Fn::Join": [ + "-", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ] + } + ] + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "-", + { + "Fn::Select": [ + 6, + { + "Fn::Split": [ + ":", + { + "Ref": "AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName83476586" + } + ] + } + ] + } + ] + } + ] + } + ] + ] + } + } + ] + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3BucketED542E1C": { + "Type": "String", + "Description": "S3 bucket for asset \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + }, + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9S3VersionKey10487FD6": { + "Type": "String", + "Description": "S3 key for asset version \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + }, + "AssetParameters2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9ArtifactHashB26239A1": { + "Type": "String", + "Description": "Artifact hash for asset \"2a2da33f11dc6085a4843d85898c13b2798393e7650fbb994d866555e23f79e9\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore new file mode 100644 index 0000000000000..033e6722bb6e0 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/.gitignore @@ -0,0 +1 @@ +!index.js diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js new file mode 100644 index 0000000000000..758813d070cc5 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.handler/index.js @@ -0,0 +1,21 @@ +/* eslint-disable no-console */ +var AWS = require('aws-sdk'); + +exports.handler = async function (event) { + const props = event.ResourceProperties; + + if (event.RequestType === 'Create' || event.RequestType === 'Update') { + const secretsmanager = new AWS.SecretsManager(); + console.log(`Secrets to validate: ${props.Secrets}`); + for (const secret of props.Secrets) { + await validateSecretNameMatchesExpected(secretsmanager, secret); + } + } +} + +async function validateSecretNameMatchesExpected(secretsmanager, secretInfo) { + const secret = await secretsmanager.describeSecret({ SecretId: secretInfo.secretArn }).promise(); + if (secretInfo.secretName !== secret.Name) { + throw new Error(`Actual secret name doesn't match expected. Actual: (${secret.Name}), Expected: (${secretInfo.secretName})`); + } +} diff --git a/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts new file mode 100644 index 0000000000000..49dde40e8b9f4 --- /dev/null +++ b/packages/@aws-cdk/aws-secretsmanager/test/integ.secret-name-parsed.ts @@ -0,0 +1,47 @@ +import * as path from 'path'; +import * as cdk from '@aws-cdk/core'; +import * as secretsmanager from '../lib'; + +/** + * Creates several secrets, with varying names and IDs, with the parseOwnedSecretName feature flag set, + * to verify the secretName returned by `Secret.secretName` matches the `Name` returned by `DescribeSecrets`. + */ + +class SecretsManagerStack extends cdk.Stack { + constructor(scope: cdk.App, id: string) { + super(scope, id); + + const defaultSecret = new secretsmanager.Secret(this, 'DefaultSecret'); + const namedSecret = new secretsmanager.Secret(this, 'NamedSecret', { secretName: 'namedSecret' }); + const namedSecretWithHyphen = new secretsmanager.Secret(this, 'NamedSecretWithHyphen', { secretName: 'named-secret-1' }); + const longSecret = new secretsmanager.Secret(this, 'AReallyLongLogicalIThatWillBeTrimmedBeforeItsUsedInTheName'); + + const secrets = [defaultSecret, namedSecret, namedSecretWithHyphen, longSecret]; + + const resourceType = 'Custom::IntegVerificationSecretNameMatches'; + const serviceToken = cdk.CustomResourceProvider.getOrCreate(this, resourceType, { + codeDirectory: path.join(__dirname, 'integ.secret-name-parsed.handler'), + runtime: cdk.CustomResourceProviderRuntime.NODEJS_12, + policyStatements: [{ + Effect: 'Allow', + Resource: secrets.map(s => s.secretArn), + Action: ['secretsmanager:DescribeSecret'], + }], + }); + new cdk.CustomResource(this, 'SecretNameVerification', { + resourceType: resourceType, + serviceToken, + properties: { + Secrets: secrets.map(s => ({ secretArn: s.secretArn, secretName: s.secretName })), + }, + }); + } +} + +const app = new cdk.App({ + context: { + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': 'true', + }, +}); +new SecretsManagerStack(app, 'Integ-SecretsManager-ParsedSecretName'); +app.synth(); diff --git a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts index 0e56a72bc3c54..22dae639500dd 100644 --- a/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts +++ b/packages/@aws-cdk/aws-secretsmanager/test/secret.test.ts @@ -1,14 +1,16 @@ import '@aws-cdk/assert/jest'; -import { ResourcePart } from '@aws-cdk/assert'; +import { expect as assertExpect, ResourcePart } from '@aws-cdk/assert'; import * as iam from '@aws-cdk/aws-iam'; import * as kms from '@aws-cdk/aws-kms'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as secretsmanager from '../lib'; +let app: cdk.App; let stack: cdk.Stack; beforeEach(() => { - stack = new cdk.Stack(); + app = new cdk.App(); + stack = new cdk.Stack(app); }); test('default secret', () => { @@ -426,18 +428,106 @@ test('secretValue', () => { }); describe('secretName', () => { - test.each([undefined, 'mySecret'])('when secretName is %s', (secretName) => { - const secret = new secretsmanager.Secret(stack, 'Secret', { - secretName, + describe('without @aws-cdk/aws-secretsmanager:parseOwnedSecretName set', () => { + test.each([undefined, 'mySecret'])('when secretName is %s', (secretName) => { + const secret = new secretsmanager.Secret(stack, 'Secret', { + secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + // Creates secret name by parsing ARN. + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }, + }); }); - new cdk.CfnOutput(stack, 'MySecretName', { - value: secret.secretName, + }); + + describe('with @aws-cdk/aws-secretsmanager:parseOwnedSecretName set', () => { + beforeEach(() => { + app = new cdk.App({ + context: { + '@aws-cdk/aws-secretsmanager:parseOwnedSecretName': 'true', + }, + }); + stack = new cdk.Stack(app); + }); + + test('selects the first two parts of the resource name when the name is auto-generated', () => { + const secret = new secretsmanager.Secret(stack, 'Secret'); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Join': ['-', [ + { 'Fn::Select': [0, { 'Fn::Split': ['-', resourceName] }] }, + { 'Fn::Select': [1, { 'Fn::Split': ['-', resourceName] }] }, + ]], + }, + }); + }); + + test('is simply the first segment when the provided secret name has no hyphens', () => { + const secret = new secretsmanager.Secret(stack, 'Secret', { secretName: 'mySecret' }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Select': [0, { 'Fn::Split': ['-', resourceName] }], + }, + }); + }); + + test.each([ + [2, 'my-secret'], + [3, 'my-secret-hyphenated'], + [4, 'my-secret-with-hyphens'], + ])('selects the %n parts of the resource name when the secret name is provided', (segments, secretName) => { + const secret = new secretsmanager.Secret(stack, 'Secret', { secretName }); + new cdk.CfnOutput(stack, 'MySecretName', { + value: secret.secretName, + }); + + const resourceName = { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }; + const secretNameSegments = []; + for (let i = 0; i < segments; i++) { + secretNameSegments.push({ 'Fn::Select': [i, { 'Fn::Split': ['-', resourceName] }] }); + } + + expect(stack).toHaveOutput({ + outputName: 'MySecretName', + outputValue: { + 'Fn::Join': ['-', secretNameSegments], + }, + }); }); - // Creates secret name by parsing ARN. - expect(stack).toHaveOutput({ - outputName: 'MySecretName', - outputValue: { 'Fn::Select': [6, { 'Fn::Split': [':', { Ref: 'SecretA720EF05' }] }] }, + test('uses existing Tokens as secret names as-is', () => { + const secret1 = new secretsmanager.Secret(stack, 'Secret1'); + const secret2 = new secretsmanager.Secret(stack, 'Secret2', { + secretName: secret1.secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName1', { + value: secret1.secretName, + }); + new cdk.CfnOutput(stack, 'MySecretName2', { + value: secret2.secretName, + }); + + const outputs = assertExpect(stack).value.Outputs; + expect(outputs.MySecretName1).toEqual(outputs.MySecretName2); }); }); }); @@ -492,7 +582,6 @@ test('import by secretArn does not strip suffixes unless the suffix length is si test('import by secretArn supports tokens for ARNs', () => { // GIVEN - const app = new cdk.App(); const stackA = new cdk.Stack(app, 'StackA'); const stackB = new cdk.Stack(app, 'StackB'); const secretA = new secretsmanager.Secret(stackA, 'SecretA'); @@ -862,7 +951,6 @@ test('can add to the resource policy of a secret', () => { test('fails if secret policy has no actions', () => { // GIVEN - const app = new cdk.App(); stack = new cdk.Stack(app, 'my-stack'); const secret = new secretsmanager.Secret(stack, 'Secret'); @@ -878,7 +966,6 @@ test('fails if secret policy has no actions', () => { test('fails if secret policy has no IAM principals', () => { // GIVEN - const app = new cdk.App(); stack = new cdk.Stack(app, 'my-stack'); const secret = new secretsmanager.Secret(stack, 'Secret'); diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index d28a88071f10d..84485ddf3c416 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -56,6 +56,15 @@ export const STACK_RELATIVE_EXPORTS_CONTEXT = '@aws-cdk/core:stackRelativeExport */ export const DOCKER_IGNORE_SUPPORT = '@aws-cdk/aws-ecr-assets:dockerIgnoreSupport'; +/** + * Secret.secretName for an "owned" secret will attempt to parse the secretName from the ARN, + * rather than the default full resource name, which includes the SecretsManager suffix. + * + * If this flag is not set, Secret.secretName will include the SecretsManager suffix, which cannot be directly + * used by SecretsManager.DescribeSecret, and must be parsed by the user first (e.g., Fn:Join, Fn:Select, Fn:Split). + */ +export const SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME = '@aws-cdk/aws-secretsmanager:parseOwnedSecretName'; + /** * This map includes context keys and values for feature flags that enable * capabilities "from the future", which we could not introduce as the default @@ -74,6 +83,7 @@ export const FUTURE_FLAGS = { [ENABLE_DIFF_NO_FAIL_CONTEXT]: 'true', [STACK_RELATIVE_EXPORTS_CONTEXT]: 'true', [DOCKER_IGNORE_SUPPORT]: true, + [SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME]: true, // We will advertise this flag when the feature is complete // [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true', @@ -89,8 +99,9 @@ const FUTURE_FLAGS_DEFAULTS: { [key: string]: boolean } = { [STACK_RELATIVE_EXPORTS_CONTEXT]: false, [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: false, [DOCKER_IGNORE_SUPPORT]: false, + [SECRETS_MANAGER_PARSE_OWNED_SECRET_NAME]: false, }; export function futureFlagDefault(flag: string): boolean { return FUTURE_FLAGS_DEFAULTS[flag]; -} \ No newline at end of file +} From 6fd049d8e405909ad80743702d0d76151d37dffa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Nov 2020 10:40:16 +0000 Subject: [PATCH 2/6] chore(deps-dev): bump @types/archiver from 3.1.1 to 5.1.0 (#11781) Bumps [@types/archiver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/archiver) from 3.1.1 to 5.1.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/archiver) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- packages/aws-cdk/package.json | 2 +- packages/cdk-assets/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/aws-cdk/package.json b/packages/aws-cdk/package.json index e66a321fc2f06..cad79354de315 100644 --- a/packages/aws-cdk/package.json +++ b/packages/aws-cdk/package.json @@ -39,7 +39,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/core": "0.0.0", - "@types/archiver": "^3.1.1", + "@types/archiver": "^5.1.0", "@types/fs-extra": "^8.1.1", "@types/glob": "^7.1.3", "@types/jest": "^26.0.15", diff --git a/packages/cdk-assets/package.json b/packages/cdk-assets/package.json index 24bc67075c70f..d2b4361fb9133 100644 --- a/packages/cdk-assets/package.json +++ b/packages/cdk-assets/package.json @@ -30,7 +30,7 @@ }, "license": "Apache-2.0", "devDependencies": { - "@types/archiver": "^3.1.1", + "@types/archiver": "^5.1.0", "@types/glob": "^7.1.3", "@types/jest": "^26.0.15", "@types/jszip": "^3.4.1", diff --git a/yarn.lock b/yarn.lock index dfe5ea7785597..51c00d7537e47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1493,10 +1493,10 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/archiver@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-3.1.1.tgz#10cc1be44af8911e57484342c7b3b32a5f178a1a" - integrity sha512-TzVZ9204sH1TuFylfr1cw/AA/3/VldAAXswEwKLXUOzA9mDg+m6gHF9EaqKNlozcjc6knX5m1KAqJzksPLSEfw== +"@types/archiver@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/archiver/-/archiver-5.1.0.tgz#869f4ce4028e49cf9a0243cf914415f4cc3d1f3d" + integrity sha512-baFOhanb/hxmcOd1Uey2TfFg43kTSmM6py1Eo7Rjbv/ivcl7PXLhY0QgXGf50Hx/eskGCFqPfhs/7IZLb15C5g== dependencies: "@types/glob" "*" From f5c177db6db3c6c27f294da85ee2bc33719a32ab Mon Sep 17 00:00:00 2001 From: Jonathan Goldwasser Date: Mon, 30 Nov 2020 13:07:06 +0100 Subject: [PATCH 3/6] chore(lambda-nodejs): ignore assets in integ tests (#11777) Ignore assets in integration tests. Updates to `esbuild` can cause the bundles to be slighlty different and what we want here is to check that we can still correctly bundle. Closes #11772 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../@aws-cdk/aws-lambda-nodejs/package.json | 2 +- .../test/integ.dependencies.expected.json | 18 ++-- .../test/integ.dependencies.ts | 1 + .../test/integ.function.expected.json | 18 ++-- .../aws-lambda-nodejs/test/integ.function.ts | 1 + yarn.lock | 102 ++++++++++++++++-- 6 files changed, 116 insertions(+), 26 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-nodejs/package.json b/packages/@aws-cdk/aws-lambda-nodejs/package.json index 2391a9015d690..afb611e22c6bb 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/package.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/package.json @@ -68,7 +68,7 @@ "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "delay": "4.4.0", - "esbuild": "^0.8.9", + "esbuild": "^0.8.17", "pkglint": "0.0.0" }, "dependencies": { diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json index 89a8de563b17f..75ecb420e7ef5 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3Bucket70E87BF4" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3BucketF29906B2" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605" + "Ref": "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12" } ] } @@ -92,17 +92,17 @@ } }, "Parameters": { - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3Bucket70E87BF4": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3BucketF29906B2": { "Type": "String", - "Description": "S3 bucket for asset \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "S3 bucket for asset \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" }, - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1S3VersionKey888B2605": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835S3VersionKey320A7F12": { "Type": "String", - "Description": "S3 key for asset version \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "S3 key for asset version \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" }, - "AssetParametersa366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1ArtifactHash01A9D743": { + "AssetParameters2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835ArtifactHash9E439F77": { "Type": "String", - "Description": "Artifact hash for asset \"a366acc095e9c09f74bf0dd7cd338214dc3b201ad849c56e33912eb3c85785e1\"" + "Description": "Artifact hash for asset \"2ff0fab1efcce787182abdd9a3c77a111379358a40cb16d90d7eb7c71ed11835\"" } } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts index 28825543a31d3..a4418867356e7 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.dependencies.ts @@ -1,3 +1,4 @@ +/// !cdk-integ pragma:ignore-assets import * as path from 'path'; import { Runtime } from '@aws-cdk/aws-lambda'; import { App, Stack, StackProps } from '@aws-cdk/core'; diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json index 97a6b1425b48a..e41f6a5cc565e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.expected.json @@ -36,7 +36,7 @@ "Properties": { "Code": { "S3Bucket": { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3BucketDF2E98AE" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3Bucket499E594B" }, "S3Key": { "Fn::Join": [ @@ -49,7 +49,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516" } ] } @@ -62,7 +62,7 @@ "Fn::Split": [ "||", { - "Ref": "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB" + "Ref": "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516" } ] } @@ -835,17 +835,17 @@ } }, "Parameters": { - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3BucketDF2E98AE": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3Bucket499E594B": { "Type": "String", - "Description": "S3 bucket for asset \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "S3 bucket for asset \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eS3VersionKey240B23FB": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcS3VersionKey0D51A516": { "Type": "String", - "Description": "S3 key for asset version \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "S3 key for asset version \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, - "AssetParameters87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756eArtifactHashC957048B": { + "AssetParameters1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dcArtifactHashECB545C0": { "Type": "String", - "Description": "Artifact hash for asset \"87629d6d123a6c9871926864abde04a406be93834dc008b18672bd287d37756e\"" + "Description": "Artifact hash for asset \"1f516d02fb984ea91e74064999e8508f09dd0001d2cf198ccc376d2c0fcd14dc\"" }, "AssetParameters565126ff75ab727f0b3a67e78cf0fa9996426d1458123e8d6ee81c7ea93a6cfaS3BucketDFAA619E": { "Type": "String", diff --git a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts index 1c700d9b4d55c..f08a041178e5e 100644 --- a/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts +++ b/packages/@aws-cdk/aws-lambda-nodejs/test/integ.function.ts @@ -1,3 +1,4 @@ +/// !cdk-integ pragma:ignore-assets import * as path from 'path'; import { Vpc } from '@aws-cdk/aws-ec2'; import { Runtime } from '@aws-cdk/aws-lambda'; diff --git a/yarn.lock b/yarn.lock index 51c00d7537e47..a47c414f52354 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2057,6 +2057,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +app-root-path@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" + integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -2294,7 +2299,7 @@ aws-sdk-mock@^5.1.0: sinon "^9.0.1" traverse "^0.6.6" -aws-sdk@^2.637.0, aws-sdk@^2.799.0: +aws-sdk@^2.596.0, aws-sdk@^2.637.0, aws-sdk@^2.799.0: version "2.799.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.799.0.tgz#8b1a64c1a9f8ccf5794eb07bdd8051e4cb6adcfd" integrity sha512-NYAoiNU+bJXhlJsC0rFqrmD5t5ho7/VxldmziP6HLPYHfOCI9Uvk6UVjfPmhLWPm0mHnIxhsHqmsNGyjhHNYmw== @@ -3840,6 +3845,16 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +dotenv-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" + integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== + +dotenv@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dotgitignore@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" @@ -4025,10 +4040,10 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild@^0.8.9: - version "0.8.9" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.9.tgz#e2b4254ee7aabb23e88c9d4abb38a16668856569" - integrity sha512-HAV4mKJqos0L8g6pL7evrw/ZPm478yFNtkuYhqJAeTrIW40XtBxhHrt4Pm2faYeRB8K6nA7dTDgmF+O0e9JCXQ== +esbuild@^0.8.17: + version "0.8.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.8.17.tgz#1c16c6d5988dcfdcf27a7e1612b7fd05e1477c54" + integrity sha512-ReHap+Iyn5BQF0B8F3xrLwu+j57ri5uDUw2ej9XTPAuFDebYiWwRzBY4jhF610bklveXLbCGim/8/2wQKQlu1w== escalade@^3.1.1: version "3.1.1" @@ -4062,6 +4077,11 @@ escodegen@^1.14.1, escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" +eslint-config-standard@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" + integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -4089,6 +4109,14 @@ eslint-module-utils@^2.6.0: debug "^2.6.9" pkg-dir "^2.0.0" +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + eslint-plugin-import@^2.22.1: version "2.22.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" @@ -4108,11 +4136,33 @@ eslint-plugin-import@^2.22.1: resolve "^1.17.0" tsconfig-paths "^3.9.0" +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + eslint-plugin-rulesdir@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-rulesdir/-/eslint-plugin-rulesdir-0.1.0.tgz#ad144d7e98464fda82963eff3fab331aecb2bf08" integrity sha1-rRRNfphGT9qClj7/P6szGuyyvwg= +eslint-plugin-standard@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz#0c3bf3a67e853f8bbbc580fb4945fbf16f41b7c5" + integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== + eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -5212,7 +5262,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4, ignore@^5.1.8: +ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -5405,6 +5455,13 @@ is-core-module@^2.0.0: dependencies: has "^1.0.3" +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -6488,6 +6545,24 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lambda-leak@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" + integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= + +lambda-tester@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" + integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== + dependencies: + app-root-path "^2.2.1" + dotenv "^8.0.0" + dotenv-json "^1.0.0" + lambda-leak "^2.0.0" + semver "^6.1.1" + uuid "^3.3.2" + vandium-utils "^1.1.1" + lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -8613,6 +8688,14 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17 dependencies: path-parse "^1.0.6" +resolve@^1.10.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + resolve@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" @@ -8770,7 +8853,7 @@ semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -10136,6 +10219,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vandium-utils@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" + integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" From 2a48cb8a29c0893a508c31d1b5937a93b92b4a98 Mon Sep 17 00:00:00 2001 From: Taliesin Millhouse Date: Mon, 30 Nov 2020 23:37:59 +1100 Subject: [PATCH 4/6] docs(cognito): fix 'sign in' doc typo (#11778) Fixed small typo. --- packages/@aws-cdk/aws-cognito/lib/user-pool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts index 3730c7c97a301..960d14bb8b426 100644 --- a/packages/@aws-cdk/aws-cognito/lib/user-pool.ts +++ b/packages/@aws-cdk/aws-cognito/lib/user-pool.ts @@ -34,7 +34,7 @@ export interface SignInAliases { readonly phone?: boolean; /** - * Whether a user is allowed to ign in with a secondary username, that can be set and modified after sign up. + * Whether a user is allowed to sign in with a secondary username, that can be set and modified after sign up. * Can only be used in conjunction with `USERNAME`. * @default false */ From 7b8b6656f909a023d8d66445b58d2d5b9dde1c15 Mon Sep 17 00:00:00 2001 From: Ayush Goyal Date: Mon, 30 Nov 2020 20:51:54 +0530 Subject: [PATCH 5/6] fix(sqs): queueUrl property has incorrect region and account for imported queue (#11651) fix(sqs): imported queue using incorrect options to generate queueUrl Currently if we use `fromQueueArn` static helper to import an existing queue, the generated `queueUrl` property uses incorrect account and region. It uses the stack's region/account in which it is imported rather than the one in which it was created. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-sqs/lib/queue.ts | 5 +++-- packages/@aws-cdk/aws-sqs/test/test.sqs.ts | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-sqs/lib/queue.ts b/packages/@aws-cdk/aws-sqs/lib/queue.ts index 2d95f60f4748e..63b466de84421 100644 --- a/packages/@aws-cdk/aws-sqs/lib/queue.ts +++ b/packages/@aws-cdk/aws-sqs/lib/queue.ts @@ -197,8 +197,9 @@ export class Queue extends QueueBase { */ public static fromQueueAttributes(scope: Construct, id: string, attrs: QueueAttributes): IQueue { const stack = Stack.of(scope); - const queueName = attrs.queueName || stack.parseArn(attrs.queueArn).resource; - const queueUrl = attrs.queueUrl || `https://sqs.${stack.region}.${stack.urlSuffix}/${stack.account}/${queueName}`; + const parsedArn = stack.parseArn(attrs.queueArn); + const queueName = attrs.queueName || parsedArn.resource; + const queueUrl = attrs.queueUrl || `https://sqs.${parsedArn.region}.${stack.urlSuffix}/${parsedArn.account}/${queueName}`; class Import extends QueueBase { public readonly queueArn = attrs.queueArn; // arn:aws:sqs:us-east-1:123456789012:queue1 diff --git a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts index afcc44beadc5b..87e38c2dcf2ca 100644 --- a/packages/@aws-cdk/aws-sqs/test/test.sqs.ts +++ b/packages/@aws-cdk/aws-sqs/test/test.sqs.ts @@ -162,7 +162,7 @@ export = { test.deepEqual(stack.resolve(imports.queueArn), 'arn:aws:sqs:us-east-1:123456789012:queue1'); test.deepEqual(stack.resolve(imports.queueUrl), { 'Fn::Join': - ['', ['https://sqs.', { Ref: 'AWS::Region' }, '.', { Ref: 'AWS::URLSuffix' }, '/', { Ref: 'AWS::AccountId' }, '/queue1']], + ['', ['https://sqs.us-east-1.', { Ref: 'AWS::URLSuffix' }, '/123456789012/queue1']], }); test.deepEqual(stack.resolve(imports.queueName), 'queue1'); test.done(); @@ -176,6 +176,25 @@ export = { test.deepEqual(fifoQueue.fifo, true); test.done(); }, + + 'importing works correctly for cross region queue'(test: Test) { + // GIVEN + const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } }); + + // WHEN + const imports = sqs.Queue.fromQueueArn(stack, 'Imported', 'arn:aws:sqs:us-west-2:123456789012:queue1'); + + // THEN + + // "import" returns an IQueue bound to `Fn::ImportValue`s. + test.deepEqual(stack.resolve(imports.queueArn), 'arn:aws:sqs:us-west-2:123456789012:queue1'); + test.deepEqual(stack.resolve(imports.queueUrl), { + 'Fn::Join': + ['', ['https://sqs.us-west-2.', { Ref: 'AWS::URLSuffix' }, '/123456789012/queue1']], + }); + test.deepEqual(stack.resolve(imports.queueName), 'queue1'); + test.done(); + }, }, 'grants': { From c072981c175bf0509e9c606ff9ed441a0c7aef31 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Mon, 30 Nov 2020 15:50:30 +0000 Subject: [PATCH 6/6] feat(ecr): authorization token retrieval grants (#11783) See README.md ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-ecr/README.md | 16 ++++++++++ packages/@aws-cdk/aws-ecr/lib/auth-token.ts | 20 ++++++++++++ packages/@aws-cdk/aws-ecr/lib/index.ts | 1 + .../@aws-cdk/aws-ecr/test/test.auth-token.ts | 31 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 packages/@aws-cdk/aws-ecr/lib/auth-token.ts create mode 100644 packages/@aws-cdk/aws-ecr/test/test.auth-token.ts diff --git a/packages/@aws-cdk/aws-ecr/README.md b/packages/@aws-cdk/aws-ecr/README.md index 97115adc5f647..a2e4ab8ee3d35 100644 --- a/packages/@aws-cdk/aws-ecr/README.md +++ b/packages/@aws-cdk/aws-ecr/README.md @@ -37,6 +37,22 @@ repository.onImageScanCompleted('ImageScanComplete') .addTarget(...) ``` +### Authorization Token + +Besides the Amazon ECR APIs, ECR also allows the Docker CLI or a language-specific Docker library to push and pull +images from an ECR repository. However, the Docker CLI does not support native IAM authentication methods and +additional steps must be taken so that Amazon ECR can authenticate and authorize Docker push and pull requests. +More information can be found at at [Registry Authentication](https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth). + +A Docker authorization token can be obtained using the `GetAuthorizationToken` ECR API. The following code snippets +grants an IAM user access to call this API. + +```ts +import * as iam from '@aws-cdk/aws-iam'; + +const user = new iam.User(this, 'User', { ... }); +AuthorizationToken.grantRead(user); +``` ### Automatically clean up repositories diff --git a/packages/@aws-cdk/aws-ecr/lib/auth-token.ts b/packages/@aws-cdk/aws-ecr/lib/auth-token.ts new file mode 100644 index 0000000000000..52c10cc513d0a --- /dev/null +++ b/packages/@aws-cdk/aws-ecr/lib/auth-token.ts @@ -0,0 +1,20 @@ +import * as iam from '@aws-cdk/aws-iam'; + +/** + * Authorization token to access ECR repositories via Docker CLI. + */ +export class AuthorizationToken { + /** + * Grant access to retrieve an authorization token. + */ + public static grantRead(grantee: iam.IGrantable) { + grantee.grantPrincipal.addToPrincipalPolicy(new iam.PolicyStatement({ + actions: ['ecr:GetAuthorizationToken'], + // GetAuthorizationToken only allows '*'. See https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelasticcontainerregistry.html#amazonelasticcontainerregistry-actions-as-permissions + resources: ['*'], + })); + } + + private constructor() { + } +} diff --git a/packages/@aws-cdk/aws-ecr/lib/index.ts b/packages/@aws-cdk/aws-ecr/lib/index.ts index bfeae449ded7c..63fff9b49a1c4 100644 --- a/packages/@aws-cdk/aws-ecr/lib/index.ts +++ b/packages/@aws-cdk/aws-ecr/lib/index.ts @@ -3,3 +3,4 @@ export * from './ecr.generated'; export * from './repository'; export * from './lifecycle'; +export * from './auth-token'; diff --git a/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts b/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts new file mode 100644 index 0000000000000..4e9e12e4fb078 --- /dev/null +++ b/packages/@aws-cdk/aws-ecr/test/test.auth-token.ts @@ -0,0 +1,31 @@ +import { expect, haveResourceLike } from '@aws-cdk/assert'; +import * as iam from '@aws-cdk/aws-iam'; +import { Stack } from '@aws-cdk/core'; +import { Test } from 'nodeunit'; +import { AuthorizationToken } from '../lib'; + +export = { + 'grant()'(test: Test) { + // GIVEN + const stack = new Stack(); + const user = new iam.User(stack, 'User'); + + // WHEN + AuthorizationToken.grantRead(user); + + // THEN + expect(stack).to(haveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'ecr:GetAuthorizationToken', + Effect: 'Allow', + Resource: '*', + }, + ], + }, + })); + + test.done(); + }, +}; \ No newline at end of file