Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-sqs): improvements to IAM grants API #1052

Merged
merged 2 commits into from
Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions packages/@aws-cdk/aws-sqs/lib/perms.ts

This file was deleted.

82 changes: 82 additions & 0 deletions packages/@aws-cdk/aws-sqs/lib/queue-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,88 @@ export abstract class QueueRef extends cdk.Construct implements s3n.IBucketNotif
dependencies: [ this.policy! ]
};
}

/**
* Grant permissions to consume messages from a queue
*
* This will grant the following permissions:
*
* - sqs:ChangeMessageVisibility
* - sqs:ChangeMessageVisibilityBatch
* - sqs:DeleteMessage
* - sqs:ReceiveMessage
* - sqs:DeleteMessageBatch
* - sqs:GetQueueAttributes
* - sqs:GetQueueUrl
*
* @param identity Principal to grant consume rights to
*/
public grantConsumeMessages(identity?: iam.IPrincipal) {
this.grant(identity,
'sqs:ReceiveMessage',
'sqs:ChangeMessageVisibility',
'sqs:ChangeMessageVisibilityBatch',
'sqs:GetQueueUrl',
'sqs:DeleteMessage',
'sqs:DeleteMessageBatch',
'sqs:GetQueueAttributes');
}

/**
* Grant access to send messages to a queue to the given identity.
*
* This will grant the following permissions:
*
* - sqs:SendMessage
* - sqs:SendMessageBatch
* - sqs:GetQueueAttributes
* - sqs:GetQueueUrl
*
* @param identity Principal to grant send rights to
*/
public grantSendMessages(identity?: iam.IPrincipal) {
this.grant(identity,
'sqs:SendMessage',
'sqs:SendMessageBatch',
'sqs:GetQueueAttributes',
'sqs:GetQueueUrl');
}

/**
* Grant an IAM principal permissions to purge all messages from the queue.
*
* This will grant the following permissions:
*
* - sqs:PurgeQueue
* - sqs:GetQueueAttributes
* - sqs:GetQueueUrl
*
* @param identity Principal to grant send rights to
* @param queueActions additional queue actions to allow
*/
public grantPurge(identity?: iam.IPrincipal) {
this.grant(identity,
'sqs:PurgeQueue',
'sqs:GetQueueAttributes',
'sqs:GetQueueUrl');
}

/**
* Grant the actions defined in queueActions to the identity Principal given
* on this SQS queue resource.
*
* @param identity Principal to grant right to
* @param queueActions The actions to grant
*/
public grant(identity?: iam.IPrincipal, ...queueActions: string[]) {
if (!identity) {
return;
}

identity.addToPolicy(new iam.PolicyStatement()
.addResource(this.queueArn)
.addActions(...queueActions));
}
}

/**
Expand Down
60 changes: 0 additions & 60 deletions packages/@aws-cdk/aws-sqs/lib/queue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import iam = require('@aws-cdk/aws-iam');
import kms = require('@aws-cdk/aws-kms');
import cdk = require('@aws-cdk/cdk');
import perms = require('./perms');
import { QueueRef } from './queue-ref';
import { cloudformation } from './sqs.generated';
import { validateProps } from './validate-props';
Expand Down Expand Up @@ -277,64 +275,6 @@ export class Queue extends QueueRef {
}
}

/**
* Grant permissions to consume messages from a queue
*
* This will grant the following permissions:
*
* - sqs:ChangeMessageVisibility
* - sqs:DeleteMessage
* - sqs:ReceiveMessage
*
* @param identity Principal to grant consume rights to
*/
public grantConsumeMessages(identity?: iam.IPrincipal) {
this.grant(identity, perms.QUEUE_GET_ACTIONS.concat(perms.QUEUE_CONSUME_ACTIONS));
}

/**
* Grant access to receive messages from a queue to
* the given identity.
*
* This will grant sqs:ReceiveMessage
*
* @param identity Principal to grant receive rights to
*/
public grantReceiveMessages(identity?: iam.IPrincipal) {
this.grant(identity, perms.QUEUE_GET_ACTIONS);
}

/**
* Grant access to send messages to a queue to the
* given identity.
*
* This will grant sqs:SendMessage
*
* @param identity Principal to grant send rights to
*/
public grantSendMessages(identity?: iam.IPrincipal) {
this.grant(identity, perms.QUEUE_PUT_ACTIONS);
}

/**
* Grant the actions defined in queueActions
* to the identity Principal given.
*
* @param identity Principal to grant right to
* @param queueActions The actions to grant
*/
private grant(identity: iam.IPrincipal | undefined,
queueActions: string[]) {

if (!identity) {
return;
}

identity.addToPolicy(new iam.PolicyStatement()
.addResource(this.queueArn)
.addActions(...queueActions));
}

/**
* Look at the props, see if the FIFO props agree, and return the correct subset of props
*/
Expand Down
146 changes: 79 additions & 67 deletions packages/@aws-cdk/aws-sqs/test/test.sqs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { expect, haveResource } from '@aws-cdk/assert';
import { ArnPrincipal, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
import iam = require('@aws-cdk/aws-iam');
import kms = require('@aws-cdk/aws-kms');
import s3 = require('@aws-cdk/aws-s3');
import { resolve, Stack } from '@aws-cdk/cdk';
import { Test } from 'nodeunit';
import sqs = require('../lib');
import { Queue } from '../lib';

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

Expand Down Expand Up @@ -56,7 +57,7 @@ export = {
'addToPolicy will automatically create a policy for this queue'(test: Test) {
const stack = new Stack();
const queue = new sqs.Queue(stack, 'MyQueue');
queue.addToResourcePolicy(new PolicyStatement().addAllResources().addActions('sqs:*').addPrincipal(new ArnPrincipal('arn')));
queue.addToResourcePolicy(new iam.PolicyStatement().addAllResources().addActions('sqs:*').addPrincipal(new iam.ArnPrincipal('arn')));
expect(stack).toMatch({
"Resources": {
"MyQueueE6CA6235": {
Expand Down Expand Up @@ -113,92 +114,77 @@ export = {
test.done();
},

'iam': {
'grants permission to consume messages'(test: Test) {
const stack = new Stack();
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
const queue = new sqs.Queue(stack, 'Queue');
queue.grantConsumeMessages(role);

expect(stack).to(haveResource('AWS::IAM::Policy', {
"PolicyDocument": {
"Statement": [
{
"Action": [
"sqs:ReceiveMessage",
"sqs:ChangeMessageVisibility",
"sqs:DeleteMessage"
],
"Effect": "Allow",
"Resource": {
"Fn::GetAtt":
[
"Queue4A7E3555",
"Arn"
]
}
}
]
}
}));

'grants': {
'grantConsumeMessages'(test: Test) {
testGrant((q, p) => q.grantConsumeMessages(p),
'sqs:ReceiveMessage',
'sqs:ChangeMessageVisibility',
'sqs:ChangeMessageVisibilityBatch',
'sqs:GetQueueUrl',
'sqs:DeleteMessage',
'sqs:DeleteMessageBatch',
'sqs:GetQueueAttributes',
);
test.done();
},

'grants permission to receive messages'(test: Test) {
const stack = new Stack();
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
const queue = new sqs.Queue(stack, 'Queue');
queue.grantReceiveMessages(role);
'grantSendMessages'(test: Test) {
testGrant((q, p) => q.grantSendMessages(p),
'sqs:SendMessage',
'sqs:SendMessageBatch',
'sqs:GetQueueAttributes',
'sqs:GetQueueUrl',
);
test.done();
},

expect(stack).to(haveResource('AWS::IAM::Policy', {
"PolicyDocument": {
"Statement": [
{
"Action": "sqs:ReceiveMessage",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt":
[
"Queue4A7E3555",
"Arn"
]
}
}
]
}
}));
'grantPurge'(test: Test) {
testGrant((q, p) => q.grantPurge(p),
'sqs:PurgeQueue',
'sqs:GetQueueAttributes',
'sqs:GetQueueUrl',
);
test.done();
},

'grant() is general purpose'(test: Test) {
testGrant((q, p) => q.grant(p, 'hello', 'world'),
'hello',
'world'
);
test.done();
},

'grants permission to send messages'(test: Test) {
'grants also work on imported queues'(test: Test) {
const stack = new Stack();
const role = new Role(stack, 'Role', { assumedBy: new ServicePrincipal('lambda.amazonaws.com') });
const queue = new sqs.Queue(stack, 'Queue');
queue.grantSendMessages(role);
const queue = Queue.import(stack, 'Import', {
queueArn: 'imported-queue-arn',
queueUrl: 'https://queue-url'
});

const user = new iam.User(stack, 'User');

queue.grantPurge(user);

expect(stack).to(haveResource('AWS::IAM::Policy', {
"PolicyDocument": {
"Statement": [
{
"Action": "sqs:SendMessage",
"Action": [
"sqs:PurgeQueue",
"sqs:GetQueueAttributes",
"sqs:GetQueueUrl"
],
"Effect": "Allow",
"Resource": {
"Fn::GetAtt":
[
"Queue4A7E3555",
"Arn"
]
}
"Resource": "imported-queue-arn"
}
]
],
"Version": "2012-10-17"
}
}));

test.done();
}

},

'queue encryption': {
Expand Down Expand Up @@ -500,3 +486,29 @@ export = {

}
};

function testGrant(action: (q: Queue, principal: iam.IPrincipal) => void, ...expectedActions: string[]) {
const stack = new Stack();
const queue = new Queue(stack, 'MyQueue');
const principal = new iam.User(stack, 'User');

action(queue, principal);

expect(stack).to(haveResource('AWS::IAM::Policy', {
"PolicyDocument": {
"Statement": [
{
"Action": expectedActions,
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"MyQueueE6CA6235",
"Arn"
]
}
}
],
"Version": "2012-10-17"
}
}));
}