Skip to content

Commit 6f2475e

Browse files
author
Elad Ben-Israel
authored
feat(aws-sqs): improvements to IAM grants API (#1052)
* feat(aws-sqs): improvements to IAM grants API Moved `grantXxx` methods from `Queue` to `QueueRef`, so they can now be performed on imported queues. Added commonly needed permissions to `grantConsumeMessages` and `grantSendMessages` such as `sqs:GetQueueAttributes`, `sqs:GetQueueUrl` and the various `sqs:xxxBatch` actions. Added support for adding arbitrary actions to each of the grant methods. Exposed `queue.grant(...actions)` as a general purpose grant method which allows users to customize the set of actions for this specific resource/principal pair. BREAKING CHANGE: `queue.grantReceiveMessages` has been removed. It is unlikely that this would be sufficient to interact with a queue. Alternatively you can use `queue.grantConsumeMessages` or `queue.grant('sqs:ReceiveMessage')` if there's a need to only grant this action.
1 parent f55d052 commit 6f2475e

File tree

4 files changed

+161
-139
lines changed

4 files changed

+161
-139
lines changed

packages/@aws-cdk/aws-sqs/lib/perms.ts

-12
This file was deleted.

packages/@aws-cdk/aws-sqs/lib/queue-ref.ts

+82
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,88 @@ export abstract class QueueRef extends cdk.Construct implements s3n.IBucketNotif
110110
dependencies: [ this.policy! ]
111111
};
112112
}
113+
114+
/**
115+
* Grant permissions to consume messages from a queue
116+
*
117+
* This will grant the following permissions:
118+
*
119+
* - sqs:ChangeMessageVisibility
120+
* - sqs:ChangeMessageVisibilityBatch
121+
* - sqs:DeleteMessage
122+
* - sqs:ReceiveMessage
123+
* - sqs:DeleteMessageBatch
124+
* - sqs:GetQueueAttributes
125+
* - sqs:GetQueueUrl
126+
*
127+
* @param identity Principal to grant consume rights to
128+
*/
129+
public grantConsumeMessages(identity?: iam.IPrincipal) {
130+
this.grant(identity,
131+
'sqs:ReceiveMessage',
132+
'sqs:ChangeMessageVisibility',
133+
'sqs:ChangeMessageVisibilityBatch',
134+
'sqs:GetQueueUrl',
135+
'sqs:DeleteMessage',
136+
'sqs:DeleteMessageBatch',
137+
'sqs:GetQueueAttributes');
138+
}
139+
140+
/**
141+
* Grant access to send messages to a queue to the given identity.
142+
*
143+
* This will grant the following permissions:
144+
*
145+
* - sqs:SendMessage
146+
* - sqs:SendMessageBatch
147+
* - sqs:GetQueueAttributes
148+
* - sqs:GetQueueUrl
149+
*
150+
* @param identity Principal to grant send rights to
151+
*/
152+
public grantSendMessages(identity?: iam.IPrincipal) {
153+
this.grant(identity,
154+
'sqs:SendMessage',
155+
'sqs:SendMessageBatch',
156+
'sqs:GetQueueAttributes',
157+
'sqs:GetQueueUrl');
158+
}
159+
160+
/**
161+
* Grant an IAM principal permissions to purge all messages from the queue.
162+
*
163+
* This will grant the following permissions:
164+
*
165+
* - sqs:PurgeQueue
166+
* - sqs:GetQueueAttributes
167+
* - sqs:GetQueueUrl
168+
*
169+
* @param identity Principal to grant send rights to
170+
* @param queueActions additional queue actions to allow
171+
*/
172+
public grantPurge(identity?: iam.IPrincipal) {
173+
this.grant(identity,
174+
'sqs:PurgeQueue',
175+
'sqs:GetQueueAttributes',
176+
'sqs:GetQueueUrl');
177+
}
178+
179+
/**
180+
* Grant the actions defined in queueActions to the identity Principal given
181+
* on this SQS queue resource.
182+
*
183+
* @param identity Principal to grant right to
184+
* @param queueActions The actions to grant
185+
*/
186+
public grant(identity?: iam.IPrincipal, ...queueActions: string[]) {
187+
if (!identity) {
188+
return;
189+
}
190+
191+
identity.addToPolicy(new iam.PolicyStatement()
192+
.addResource(this.queueArn)
193+
.addActions(...queueActions));
194+
}
113195
}
114196

115197
/**

packages/@aws-cdk/aws-sqs/lib/queue.ts

-60
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import iam = require('@aws-cdk/aws-iam');
21
import kms = require('@aws-cdk/aws-kms');
32
import cdk = require('@aws-cdk/cdk');
4-
import perms = require('./perms');
53
import { QueueRef } from './queue-ref';
64
import { cloudformation } from './sqs.generated';
75
import { validateProps } from './validate-props';
@@ -277,64 +275,6 @@ export class Queue extends QueueRef {
277275
}
278276
}
279277

280-
/**
281-
* Grant permissions to consume messages from a queue
282-
*
283-
* This will grant the following permissions:
284-
*
285-
* - sqs:ChangeMessageVisibility
286-
* - sqs:DeleteMessage
287-
* - sqs:ReceiveMessage
288-
*
289-
* @param identity Principal to grant consume rights to
290-
*/
291-
public grantConsumeMessages(identity?: iam.IPrincipal) {
292-
this.grant(identity, perms.QUEUE_GET_ACTIONS.concat(perms.QUEUE_CONSUME_ACTIONS));
293-
}
294-
295-
/**
296-
* Grant access to receive messages from a queue to
297-
* the given identity.
298-
*
299-
* This will grant sqs:ReceiveMessage
300-
*
301-
* @param identity Principal to grant receive rights to
302-
*/
303-
public grantReceiveMessages(identity?: iam.IPrincipal) {
304-
this.grant(identity, perms.QUEUE_GET_ACTIONS);
305-
}
306-
307-
/**
308-
* Grant access to send messages to a queue to the
309-
* given identity.
310-
*
311-
* This will grant sqs:SendMessage
312-
*
313-
* @param identity Principal to grant send rights to
314-
*/
315-
public grantSendMessages(identity?: iam.IPrincipal) {
316-
this.grant(identity, perms.QUEUE_PUT_ACTIONS);
317-
}
318-
319-
/**
320-
* Grant the actions defined in queueActions
321-
* to the identity Principal given.
322-
*
323-
* @param identity Principal to grant right to
324-
* @param queueActions The actions to grant
325-
*/
326-
private grant(identity: iam.IPrincipal | undefined,
327-
queueActions: string[]) {
328-
329-
if (!identity) {
330-
return;
331-
}
332-
333-
identity.addToPolicy(new iam.PolicyStatement()
334-
.addResource(this.queueArn)
335-
.addActions(...queueActions));
336-
}
337-
338278
/**
339279
* Look at the props, see if the FIFO props agree, and return the correct subset of props
340280
*/

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

+79-67
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { expect, haveResource } from '@aws-cdk/assert';
2-
import { ArnPrincipal, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
2+
import iam = require('@aws-cdk/aws-iam');
33
import kms = require('@aws-cdk/aws-kms');
44
import s3 = require('@aws-cdk/aws-s3');
55
import { resolve, Stack } from '@aws-cdk/cdk';
66
import { Test } from 'nodeunit';
77
import sqs = require('../lib');
8+
import { Queue } from '../lib';
89

910
// tslint:disable:object-literal-key-quotes
1011

@@ -56,7 +57,7 @@ export = {
5657
'addToPolicy will automatically create a policy for this queue'(test: Test) {
5758
const stack = new Stack();
5859
const queue = new sqs.Queue(stack, 'MyQueue');
59-
queue.addToResourcePolicy(new PolicyStatement().addAllResources().addActions('sqs:*').addPrincipal(new ArnPrincipal('arn')));
60+
queue.addToResourcePolicy(new iam.PolicyStatement().addAllResources().addActions('sqs:*').addPrincipal(new iam.ArnPrincipal('arn')));
6061
expect(stack).toMatch({
6162
"Resources": {
6263
"MyQueueE6CA6235": {
@@ -113,92 +114,77 @@ export = {
113114
test.done();
114115
},
115116

116-
'iam': {
117-
'grants permission to consume messages'(test: Test) {
118-
const stack = new Stack();
119-
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
120-
const queue = new sqs.Queue(stack, 'Queue');
121-
queue.grantConsumeMessages(role);
122-
123-
expect(stack).to(haveResource('AWS::IAM::Policy', {
124-
"PolicyDocument": {
125-
"Statement": [
126-
{
127-
"Action": [
128-
"sqs:ReceiveMessage",
129-
"sqs:ChangeMessageVisibility",
130-
"sqs:DeleteMessage"
131-
],
132-
"Effect": "Allow",
133-
"Resource": {
134-
"Fn::GetAtt":
135-
[
136-
"Queue4A7E3555",
137-
"Arn"
138-
]
139-
}
140-
}
141-
]
142-
}
143-
}));
144-
117+
'grants': {
118+
'grantConsumeMessages'(test: Test) {
119+
testGrant((q, p) => q.grantConsumeMessages(p),
120+
'sqs:ReceiveMessage',
121+
'sqs:ChangeMessageVisibility',
122+
'sqs:ChangeMessageVisibilityBatch',
123+
'sqs:GetQueueUrl',
124+
'sqs:DeleteMessage',
125+
'sqs:DeleteMessageBatch',
126+
'sqs:GetQueueAttributes',
127+
);
145128
test.done();
146129
},
147130

148-
'grants permission to receive messages'(test: Test) {
149-
const stack = new Stack();
150-
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
151-
const queue = new sqs.Queue(stack, 'Queue');
152-
queue.grantReceiveMessages(role);
131+
'grantSendMessages'(test: Test) {
132+
testGrant((q, p) => q.grantSendMessages(p),
133+
'sqs:SendMessage',
134+
'sqs:SendMessageBatch',
135+
'sqs:GetQueueAttributes',
136+
'sqs:GetQueueUrl',
137+
);
138+
test.done();
139+
},
153140

154-
expect(stack).to(haveResource('AWS::IAM::Policy', {
155-
"PolicyDocument": {
156-
"Statement": [
157-
{
158-
"Action": "sqs:ReceiveMessage",
159-
"Effect": "Allow",
160-
"Resource": {
161-
"Fn::GetAtt":
162-
[
163-
"Queue4A7E3555",
164-
"Arn"
165-
]
166-
}
167-
}
168-
]
169-
}
170-
}));
141+
'grantPurge'(test: Test) {
142+
testGrant((q, p) => q.grantPurge(p),
143+
'sqs:PurgeQueue',
144+
'sqs:GetQueueAttributes',
145+
'sqs:GetQueueUrl',
146+
);
147+
test.done();
148+
},
171149

150+
'grant() is general purpose'(test: Test) {
151+
testGrant((q, p) => q.grant(p, 'hello', 'world'),
152+
'hello',
153+
'world'
154+
);
172155
test.done();
173156
},
174157

175-
'grants permission to send messages'(test: Test) {
158+
'grants also work on imported queues'(test: Test) {
176159
const stack = new Stack();
177-
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
178-
const queue = new sqs.Queue(stack, 'Queue');
179-
queue.grantSendMessages(role);
160+
const queue = Queue.import(stack, 'Import', {
161+
queueArn: 'imported-queue-arn',
162+
queueUrl: 'https://queue-url'
163+
});
164+
165+
const user = new iam.User(stack, 'User');
166+
167+
queue.grantPurge(user);
180168

181169
expect(stack).to(haveResource('AWS::IAM::Policy', {
182170
"PolicyDocument": {
183171
"Statement": [
184172
{
185-
"Action": "sqs:SendMessage",
173+
"Action": [
174+
"sqs:PurgeQueue",
175+
"sqs:GetQueueAttributes",
176+
"sqs:GetQueueUrl"
177+
],
186178
"Effect": "Allow",
187-
"Resource": {
188-
"Fn::GetAtt":
189-
[
190-
"Queue4A7E3555",
191-
"Arn"
192-
]
193-
}
179+
"Resource": "imported-queue-arn"
194180
}
195-
]
181+
],
182+
"Version": "2012-10-17"
196183
}
197184
}));
198185

199186
test.done();
200187
}
201-
202188
},
203189

204190
'queue encryption': {
@@ -500,3 +486,29 @@ export = {
500486

501487
}
502488
};
489+
490+
function testGrant(action: (q: Queue, principal: iam.IPrincipal) => void, ...expectedActions: string[]) {
491+
const stack = new Stack();
492+
const queue = new Queue(stack, 'MyQueue');
493+
const principal = new iam.User(stack, 'User');
494+
495+
action(queue, principal);
496+
497+
expect(stack).to(haveResource('AWS::IAM::Policy', {
498+
"PolicyDocument": {
499+
"Statement": [
500+
{
501+
"Action": expectedActions,
502+
"Effect": "Allow",
503+
"Resource": {
504+
"Fn::GetAtt": [
505+
"MyQueueE6CA6235",
506+
"Arn"
507+
]
508+
}
509+
}
510+
],
511+
"Version": "2012-10-17"
512+
}
513+
}));
514+
}

0 commit comments

Comments
 (0)