/// <summary> /// Compact SQS access policy /// </summary> /// <para> /// Transforms policies with multiple similar statements: /// <code> /// { /// "Version": "2012-10-17", /// "Statement": [ /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": "arn:aws:sns:us-east-1:FirstTopic" /// } /// } /// }, /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": "arn:aws:sns:us-east-1:SecondTopic" /// } /// } /// }] /// } /// </code> /// into compacted single statement: /// <code> /// { /// "Version": "2012-10-17", /// "Statement": [ /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": [ /// "arn:aws:sns:us-east-1:FirstTopic", /// "arn:aws:sns:us-east-1:SecondTopic" /// ] /// } /// } /// }] /// } /// </code> /// </para> /// <param name="policy"></param> /// <param name="sqsQueueArn"></param> public static void CompactSqsPermissions(this Policy policy, string sqsQueueArn) { var statementsToCompact = policy.Statements .Where(s => s.Effect == Statement.StatementEffect.Allow) #pragma warning disable CS0618 // Type or member is obsolete .Where(s => s.Actions.All(a => string.Equals(a.ActionName, SQSActionIdentifiers.SendMessage.ActionName, StringComparison.OrdinalIgnoreCase))) #pragma warning restore CS0618 // Type or member is obsolete .Where(s => s.Resources.All(r => string.Equals(r.Id, sqsQueueArn, StringComparison.OrdinalIgnoreCase))) .Where(s => s.Principals.All(r => string.Equals(r.Id, "*", StringComparison.OrdinalIgnoreCase))) .ToList(); if (statementsToCompact.Count < 2) { return; } var topicArns = new HashSet <string>(); foreach (var statement in statementsToCompact) { policy.Statements.Remove(statement); foreach (var topicArn in statement.Conditions.SelectMany(c => c.Values)) { topicArns.Add(topicArn); } } policy.AddSqsPermissions(topicArns, sqsQueueArn); }
/// <summary> /// Compact SQS access policy /// </summary> /// <para> /// Transforms policies with multiple similar statements: /// <code> /// { /// "Version": "2012-10-17", /// "Statement": [ /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue-FirstTopic" /// } /// } /// }, /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue-SecondTopic" /// } /// } /// }, /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": "arn:aws:sns:us-east-1:MyQueue2-FirstTopic" /// } /// } /// },] /// } /// </code> /// into compacted single statement: /// <code> /// { /// "Version": "2012-10-17", /// "Statement": [ /// { /// "Effect": "Allow", /// "Principal": { /// "AWS": "*" /// }, /// "Action": "sqs:SendMessage", /// "Resource": "arn:aws:sqs:us-east-1:MyQueue-v1", /// "Condition": { /// "ArnLike": { /// "aws:SourceArn": [ /// "arn:aws:sns:us-east-1:MyQueue-*", /// "arn:aws:sns:us-east-1:MyQueue2-FirstTopic" /// ] /// } /// } /// }] /// } /// </code> /// </para> /// <param name="policy"></param> /// <param name="sqsQueueArn"></param> public static void CompactSqsPermissions(this Policy policy, string sqsQueueArn) { var statementsToCompact = policy.Statements .Where(s => s.Effect == Statement.StatementEffect.Allow) #pragma warning disable CS0618 // Type or member is obsolete .Where(s => s.Actions.All(a => string.Equals(a.ActionName, SQSActionIdentifiers.SendMessage.ActionName, StringComparison.OrdinalIgnoreCase))) #pragma warning restore CS0618 // Type or member is obsolete .Where(s => s.Resources.All(r => string.Equals(r.Id, sqsQueueArn, StringComparison.OrdinalIgnoreCase))) .Where(s => s.Principals.All(r => string.Equals(r.Id, "*", StringComparison.OrdinalIgnoreCase))) .ToList(); var groupName = GetGroupName(sqsQueueArn); if (groupName != null) { groupName = $":{groupName}-"; } if (statementsToCompact.Count < 2 && groupName == null) { return; } var topicArns = new HashSet <string>(); foreach (var statement in statementsToCompact) { policy.Statements.Remove(statement); foreach (var topicArn in statement.Conditions.SelectMany(c => c.Values)) { topicArns.Add( groupName != null && topicArn.Contains(groupName, StringComparison.InvariantCultureIgnoreCase) ? $"{GetArnGroupPrefix(topicArn)}-*" : topicArn); } } policy.AddSqsPermissions(topicArns.OrderBy(a => a), sqsQueueArn); }