Skip to content

Commit 0d9c300

Browse files
authored
feat(sns): enable passing PolicyDocument to TopicPolicy (#10559)
Adds optional `policyDocument` prop to `TopicPolicyProps` to allow passing existing policy documents. fixes #7934 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent e5297b1 commit 0d9c300

File tree

3 files changed

+139
-0
lines changed

3 files changed

+139
-0
lines changed

packages/@aws-cdk/aws-sns/README.md

+40
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,43 @@ codeCommitRepository.onCommit(new targets.SnsTopic(myTopic));
131131

132132
This will result in adding a target to the event rule and will also modify the
133133
topic resource policy to allow CloudWatch events to publish to the topic.
134+
135+
## Topic Policy
136+
137+
A topic policy is automatically created when `addToResourcePolicy` is called, if
138+
one doesn't already exist. Using `addToResourcePolicy` is the simplest way to
139+
add policies, but a `TopicPolicy` can also be created manually.
140+
141+
```ts
142+
const topic = new sns.Topic(stack, 'Topic');
143+
const topicPolicy = new sns.TopicPolicy(stack, 'TopicPolicy', {
144+
topics: [topic],
145+
});
146+
147+
topicPolicy.document.addStatements(new iam.PolicyStatement({
148+
actions: ["sns:Subscribe"],
149+
principals: [new iam.AnyPrincipal()],
150+
resources: [topic.topicArn],
151+
}));
152+
```
153+
154+
A policy document can also be passed on `TopicPolicy` construction
155+
156+
```ts
157+
const topic = new sns.Topic(stack, 'Topic');
158+
const policyDocument = new iam.PolicyDocument({
159+
assignSids: true,
160+
statements: [
161+
new iam.PolicyStatement({
162+
actions: ["sns:Subscribe"],
163+
principals: [new iam.AnyPrincipal()],
164+
resources: [topic.topicArn]
165+
}),
166+
],
167+
});
168+
169+
const topicPolicy = new sns.TopicPolicy(this, 'Policy', {
170+
topics: [topic],
171+
policyDocument,
172+
});
173+
```

packages/@aws-cdk/aws-sns/lib/policy.ts

+8
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ export interface TopicPolicyProps {
1212
* The set of topics this policy applies to.
1313
*/
1414
readonly topics: ITopic[];
15+
/**
16+
* IAM policy document to apply to topic(s).
17+
* @default empty policy document
18+
*/
19+
readonly policyDocument?: PolicyDocument;
20+
1521
}
1622

1723
/**
@@ -32,6 +38,8 @@ export class TopicPolicy extends Resource {
3238
constructor(scope: Construct, id: string, props: TopicPolicyProps) {
3339
super(scope, id);
3440

41+
this.document = props.policyDocument ?? this.document;
42+
3543
new CfnTopicPolicy(this, 'Resource', {
3644
policyDocument: this.document,
3745
topics: props.topics.map(t => t.topicArn),

packages/@aws-cdk/aws-sns/test/test.sns.ts

+91
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,97 @@ export = {
276276
test.done();
277277
},
278278

279+
'TopicPolicy passed document'(test: Test) {
280+
// GIVEN
281+
const stack = new cdk.Stack();
282+
const topic = new sns.Topic(stack, 'MyTopic');
283+
const ps = new iam.PolicyStatement({
284+
actions: ['service:statement0'],
285+
principals: [new iam.ArnPrincipal('arn')],
286+
});
287+
288+
// WHEN
289+
new sns.TopicPolicy(stack, 'topicpolicy', { topics: [topic], policyDocument: new iam.PolicyDocument({ assignSids: true, statements: [ps] }) });
290+
291+
// THEN
292+
expect(stack).toMatch({
293+
'Resources': {
294+
'MyTopic86869434': {
295+
'Type': 'AWS::SNS::Topic',
296+
},
297+
'topicpolicyF8CF12FD': {
298+
'Type': 'AWS::SNS::TopicPolicy',
299+
'Properties': {
300+
'PolicyDocument': {
301+
'Statement': [
302+
{
303+
'Action': 'service:statement0',
304+
'Effect': 'Allow',
305+
'Principal': { 'AWS': 'arn' },
306+
'Sid': '0',
307+
},
308+
],
309+
'Version': '2012-10-17',
310+
},
311+
'Topics': [
312+
{
313+
'Ref': 'MyTopic86869434',
314+
},
315+
],
316+
},
317+
},
318+
},
319+
});
320+
321+
test.done();
322+
},
323+
324+
'Add statements to policy'(test: Test) {
325+
// GIVEN
326+
const stack = new cdk.Stack();
327+
const topic = new sns.Topic(stack, 'MyTopic');
328+
329+
// WHEN
330+
const topicPolicy = new sns.TopicPolicy(stack, 'TopicPolicy', {
331+
topics: [topic],
332+
});
333+
topicPolicy.document.addStatements(new iam.PolicyStatement({
334+
actions: ['service:statement0'],
335+
principals: [new iam.ArnPrincipal('arn')],
336+
}));
337+
338+
// THEN
339+
expect(stack).toMatch({
340+
'Resources': {
341+
'MyTopic86869434': {
342+
'Type': 'AWS::SNS::Topic',
343+
},
344+
'TopicPolicyA24B096F': {
345+
'Type': 'AWS::SNS::TopicPolicy',
346+
'Properties': {
347+
'PolicyDocument': {
348+
'Statement': [
349+
{
350+
'Action': 'service:statement0',
351+
'Effect': 'Allow',
352+
'Principal': { 'AWS': 'arn' },
353+
'Sid': '0',
354+
},
355+
],
356+
'Version': '2012-10-17',
357+
},
358+
'Topics': [
359+
{
360+
'Ref': 'MyTopic86869434',
361+
},
362+
],
363+
},
364+
},
365+
},
366+
});
367+
test.done();
368+
},
369+
279370
'topic resource policy includes unique SIDs'(test: Test) {
280371
const stack = new cdk.Stack();
281372

0 commit comments

Comments
 (0)