diff --git a/packages/sns/lib/sns/AbstractSnsService.ts b/packages/sns/lib/sns/AbstractSnsService.ts index b68a34b3..bae12046 100644 --- a/packages/sns/lib/sns/AbstractSnsService.ts +++ b/packages/sns/lib/sns/AbstractSnsService.ts @@ -29,7 +29,7 @@ export type SNSTopicConfig = { } export type ExtraSNSCreationParams = { - queueUrlsWithSubscribePermissionsPrefix?: string + queueUrlsWithSubscribePermissionsPrefix?: string | readonly string[] allowedSourceOwner?: string } diff --git a/packages/sns/lib/utils/snsAttributeUtils.spec.ts b/packages/sns/lib/utils/snsAttributeUtils.spec.ts index 1244529c..96f78526 100644 --- a/packages/sns/lib/utils/snsAttributeUtils.spec.ts +++ b/packages/sns/lib/utils/snsAttributeUtils.spec.ts @@ -16,7 +16,32 @@ describe('snsAttributeUtils', () => { }) expect(resolvedPolicy).toBe( - `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{"StringEquals":{"AWS:SourceOwner": "111111111111"},"StringLike":{"sns:Endpoint":"arn:aws:sqs:eu-central-1:632374391739:test-sqs-*"}}}]}`, + `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{"StringEquals":{"AWS:SourceOwner":"111111111111"},"StringLike":{"sns:Endpoint":"arn:aws:sqs:eu-central-1:632374391739:test-sqs-*"}}}]}`, + ) + }) + + it('resolves policy for array of sns:endpoints', () => { + const resolvedPolicy = generateTopicSubscriptionPolicy({ + topicArn: 'arn:aws:sns:eu-central-1:632374391739:test-sns-some-service', + allowedSqsQueueUrlPrefix: [ + 'arn:aws:sqs:eu-central-1:632374391739:test1-sqs-*', + 'arn:aws:sqs:eu-central-1:632374391739:test2-sqs-*', + ], + }) + + expect(resolvedPolicy).toBe( + `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{"StringLike":{"sns:Endpoint":["arn:aws:sqs:eu-central-1:632374391739:test1-sqs-*","arn:aws:sqs:eu-central-1:632374391739:test2-sqs-*"]}}}]}`, + ) + }) + + it('resolves policy without condition for sns:endpoint if provided array is empty', () => { + const resolvedPolicy = generateTopicSubscriptionPolicy({ + topicArn: 'arn:aws:sns:eu-central-1:632374391739:test-sns-some-service', + allowedSqsQueueUrlPrefix: [], + }) + + expect(resolvedPolicy).toBe( + `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{}}]}`, ) }) @@ -27,7 +52,7 @@ describe('snsAttributeUtils', () => { }) expect(resolvedPolicy).toBe( - `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{"StringEquals":{"AWS:SourceOwner": "111111111111"}}}]}`, + `{"Version":"2012-10-17","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"arn:aws:sns:eu-central-1:632374391739:test-sns-some-service","Condition":{"StringEquals":{"AWS:SourceOwner":"111111111111"}}}]}`, ) }) diff --git a/packages/sns/lib/utils/snsAttributeUtils.ts b/packages/sns/lib/utils/snsAttributeUtils.ts index b02ae029..98b8369d 100644 --- a/packages/sns/lib/utils/snsAttributeUtils.ts +++ b/packages/sns/lib/utils/snsAttributeUtils.ts @@ -5,21 +5,42 @@ const POLICY_VERSION = '2012-10-17' export type TopicSubscriptionPolicyParams = { topicArn: string - allowedSqsQueueUrlPrefix?: string + allowedSqsQueueUrlPrefix?: string | readonly string[] allowedSourceOwner?: string } export function generateTopicSubscriptionPolicy(params: TopicSubscriptionPolicyParams) { - const sourceOwnerFragment = params.allowedSourceOwner - ? `"StringEquals":{"AWS:SourceOwner": "${params.allowedSourceOwner}"}` - : '' - const supportedSqsQueueUrlPrefixFragment = params.allowedSqsQueueUrlPrefix - ? `"StringLike":{"sns:Endpoint":"${params.allowedSqsQueueUrlPrefix}"}` - : '' - const commaFragment = - sourceOwnerFragment.length > 0 && supportedSqsQueueUrlPrefixFragment.length > 0 ? ',' : '' + const policyObject = { + Version: POLICY_VERSION, + Id: '__default_policy_ID', + Statement: [ + { + Sid: 'AllowSQSSubscription', + Effect: 'Allow', + Principal: { + AWS: '*', + }, + Action: ['sns:Subscribe'], + Resource: params.topicArn, + Condition: {}, + }, + ], + } + + if (params.allowedSourceOwner) { + // @ts-ignore + policyObject.Statement[0].Condition.StringEquals = { + 'AWS:SourceOwner': params.allowedSourceOwner, + } + } + if (params.allowedSqsQueueUrlPrefix?.length && params.allowedSqsQueueUrlPrefix.length > 0) { + // @ts-ignore + policyObject.Statement[0].Condition.StringLike = { + 'sns:Endpoint': params.allowedSqsQueueUrlPrefix, + } + } - return `{"Version":"${POLICY_VERSION}","Id":"__default_policy_ID","Statement":[{"Sid":"AllowSQSSubscription","Effect":"Allow","Principal":{"AWS":"*"},"Action":["sns:Subscribe"],"Resource":"${params.topicArn}","Condition":{${sourceOwnerFragment}${commaFragment}${supportedSqsQueueUrlPrefixFragment}}}]}` + return JSON.stringify(policyObject) } export function generateFilterAttributes(