Skip to content

Commit

Permalink
Merge branch 'mr/forestier/fix-sns-topic-policy' into 'master'
Browse files Browse the repository at this point in the history
Fix TopicPolicy and QueuePolicy generation

See merge request it/e3-aws!5
  • Loading branch information
jeromef853 committed Jul 19, 2024
2 parents c4222ac + b2c68c7 commit 63499e4
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 68 deletions.
24 changes: 10 additions & 14 deletions src/e3/aws/troposphere/s3/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ def add_notification_configuration(
) -> None:
"""Add a configuration to bucket notification rules.
:param event: the S3 bucket event for which to invoke the Lambda function
:param function: function to invoke when the specified event type occurs
:param event: the S3 bucket event for which to invoke or notify the target
:param target: target to invoke or notify when the specified event type occurs
:param permission_suffix: a name suffix for permissions or policy objects
:param s3_filter: the filtering rules that determine which objects invoke
the AWS Lambda function
or notify the target
"""
params = {"Event": event}
if s3_filter:
Expand Down Expand Up @@ -132,7 +132,7 @@ def add_notification_configuration(
def notification_setup(
self,
) -> tuple[s3.NotificationConfiguration, list[AWSObject]]:
"""Return notifcation configuration and associated resources."""
"""Return notification configuration and associated resources."""
notification_resources = []
notification_config = None
params = {}
Expand Down Expand Up @@ -166,15 +166,13 @@ def notification_setup(
}
)
# Add policy allowing to publish to topics
for _, topic, suffix in self.topic_configurations:
for _, topic, _ in self.topic_configurations:
if topic:
topic_policy = topic.allow_publish_policy(
topic_policy_name = topic.add_allow_service_to_publish_statement(
service="s3",
name_suffix=suffix,
condition={"ArnLike": {"aws:SourceArn": self.arn}},
)
notification_resources.append(topic_policy)
self.depends_on.append(topic_policy)
self.depends_on.append(topic_policy_name)
if self.queue_configurations:
params.update(
{
Expand All @@ -184,15 +182,13 @@ def notification_setup(
]
}
)
for _, queue, suffix in self.queue_configurations:
for _, queue, _ in self.queue_configurations:
if queue:
queue_policy = queue.allow_service_to_write(
queue_policy_name = queue.add_allow_service_to_write_statement(
service="s3",
name_suffix=suffix,
condition={"ArnLike": {"aws:SourceArn": self.arn}},
)
notification_resources.append(queue_policy)
self.depends_on.append(queue_policy)
self.depends_on.append(queue_policy_name)

if params:
notification_config = s3.NotificationConfiguration(
Expand Down
52 changes: 33 additions & 19 deletions src/e3/aws/troposphere/sns/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from e3.aws import name_to_id
from e3.aws.troposphere import Construct
from e3.aws.troposphere.iam.policy_document import PolicyDocument
from e3.aws.troposphere.iam.policy_statement import Allow
from e3.aws.troposphere.iam.policy_statement import Allow, PolicyStatement

from troposphere import sns, GetAtt, Ref

Expand All @@ -29,6 +29,11 @@ def __init__(self, name: str, kms_master_key_id: str | None = None):
self.subscriptions: list[sns.Subscription] = []
self.optional_resources: list[AWSObject] = []
self.kms_master_key_id = kms_master_key_id
self.topic_policy_statements: list[PolicyStatement] = []

def _get_topic_policy_name(self) -> str:
"""Return the TopicPolicy name."""
return name_to_id(f"{self.name}Policy")

def add_lambda_subscription(
self, function: Function, delivery_policy: dict | None = None
Expand Down Expand Up @@ -58,29 +63,24 @@ def add_lambda_subscription(
]
)

def allow_publish_policy(
self, service: str, name_suffix: str, condition: ConditionType | None = None
) -> sns.TopicPolicy:
"""Return a policy allowing a service to publish to the topic.
def add_allow_service_to_publish_statement(
self, service: str, condition: ConditionType | None = None
) -> str:
"""Add a statement in TopicPolicy allowing a service to publish to the topic.
:param service: service allowed to publish
:param name_suffix: a suffix used in the object name
:param condition: condition to be able to publish
:return: the TopicPolicy name for depends_on settings
"""
return sns.TopicPolicy(
name_to_id(f"{self.name}Policy{name_suffix}"),
Topics=[self.ref],
PolicyDocument=PolicyDocument(
statements=[
Allow(
action="sns:Publish",
resource=self.ref,
principal={"Service": f"{service}.amazonaws.com"},
condition=condition,
)
]
).as_dict,
self.topic_policy_statements.append(
Allow(
action="sns:Publish",
resource=self.ref,
principal={"Service": f"{service}.amazonaws.com"},
condition=condition,
)
)
return self._get_topic_policy_name()

@property
def arn(self) -> GetAtt:
Expand All @@ -99,6 +99,20 @@ def resources(self, stack: Stack) -> list[AWSObject]:
if self.kms_master_key_id is not None:
params["KmsMasterKeyId"] = self.kms_master_key_id

# Add Topic policy to optional resources if any statement
if self.topic_policy_statements:
self.optional_resources.extend(
[
sns.TopicPolicy(
self._get_topic_policy_name(),
Topics=[self.ref],
PolicyDocument=PolicyDocument(
statements=self.topic_policy_statements,
).as_dict,
)
]
)

return [
sns.Topic(
name_to_id(self.name),
Expand Down
72 changes: 44 additions & 28 deletions src/e3/aws/troposphere/sqs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from e3.aws import name_to_id
from e3.aws.troposphere import Construct
from e3.aws.troposphere.iam.policy_document import PolicyDocument
from e3.aws.troposphere.iam.policy_statement import Allow
from e3.aws.troposphere.iam.policy_statement import Allow, PolicyStatement

from troposphere import sns, sqs, GetAtt, Ref

Expand All @@ -15,7 +15,7 @@


class Queue(Construct):
"""A SQS Topic."""
"""A SQS Queue."""

def __init__(
self,
Expand All @@ -26,7 +26,7 @@ def __init__(
) -> None:
"""Initialize a SQS.
:param name: topic name
:param name: queue name
:param fifo: Set the queue type to fifo
:param visibility_timeout: set the length of time during which a message will be
unavailable after a message is delivered from the queue
Expand All @@ -49,25 +49,30 @@ def __init__(
"maxReceiveCount": "3",
}
self.optional_resources: list[AWSObject] = []
self.queue_policy_statements: list[PolicyStatement] = []

def allow_service_to_write(
self, service: str, name_suffix: str, condition: Optional[ConditionType] = None
) -> sqs.QueuePolicy:
"""Enable a given service to send a message."""
return sqs.QueuePolicy(
name_to_id(f"{self.name}Policy{name_suffix}"),
Queues=[self.ref],
PolicyDocument=PolicyDocument(
statements=[
Allow(
action="sqs:SendMessage",
resource=self.arn,
principal={"Service": f"{service}.amazonaws.com"},
condition=condition,
)
]
).as_dict,
def _get_queue_policy_name(self) -> str:
"""Return the QueuePolicy name."""
return name_to_id(f"{self.name}Policy")

def add_allow_service_to_write_statement(
self, service: str, condition: Optional[ConditionType] = None
) -> str:
"""Add a statement in QueuePolicy allowing a service to send msg to the queue.
:param service: service allowed to send message
:param condition: condition to be able to send message
:return: the QueuePolicy name for depends_on settings
"""
self.queue_policy_statements.append(
Allow(
action="sqs:SendMessage",
resource=self.arn,
principal={"Service": f"{service}.amazonaws.com"},
condition=condition,
)
)
return self._get_queue_policy_name()

def subscribe_to_sns_topic(
self, topic_arn: str, delivery_policy: dict | None = None
Expand All @@ -87,15 +92,13 @@ def subscribe_to_sns_topic(
if delivery_policy:
sub_params.update({"DeliveryPolicy": delivery_policy})

self.add_allow_service_to_write_statement(
service="sns",
condition={"ArnLike": {"aws:SourceArn": topic_arn}},
)

self.optional_resources.extend(
[
sns.SubscriptionResource(name_to_id(f"{self.name}Sub"), **sub_params),
self.allow_service_to_write(
service="sns",
name_suffix="Sub",
condition={"ArnLike": {"aws:SourceArn": topic_arn}},
),
]
[sns.SubscriptionResource(name_to_id(f"{self.name}Sub"), **sub_params)]
)

@property
Expand All @@ -110,6 +113,19 @@ def ref(self) -> Ref:

def resources(self, stack: Stack) -> list[AWSObject]:
"""Compute AWS resources for the construct."""
# Add Queue policy to optional resources if any statement
if self.queue_policy_statements:
self.optional_resources.extend(
[
sqs.QueuePolicy(
self._get_queue_policy_name(),
Queues=[self.ref],
PolicyDocument=PolicyDocument(
statements=self.queue_policy_statements
).as_dict,
)
]
)
return [
sqs.Queue.from_dict(name_to_id(self.name), self.attr),
*self.optional_resources,
Expand Down
8 changes: 4 additions & 4 deletions tests/tests_e3_aws/troposphere/s3/bucket.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@
},
"Type": "AWS::S3::Bucket",
"DependsOn": [
"TestTopicPolicyTpUpload",
"TestQueuePolicyFileEvent"
"TestTopicPolicy",
"TestQueuePolicy"
]
},
"TestBucketPolicy": {
Expand Down Expand Up @@ -129,7 +129,7 @@
},
"Type": "AWS::Lambda::Permission"
},
"TestTopicPolicyTpUpload": {
"TestTopicPolicy": {
"Properties": {
"Topics": [
{
Expand Down Expand Up @@ -159,7 +159,7 @@
},
"Type": "AWS::SNS::TopicPolicy"
},
"TestQueuePolicyFileEvent": {
"TestQueuePolicy": {
"Properties": {
"PolicyDocument": {
"Statement": [
Expand Down
4 changes: 2 additions & 2 deletions tests/tests_e3_aws/troposphere/s3/s3_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ def test_bucket(stack: Stack) -> None:
handler="app.main",
)

stack.add(topic_test)
stack.add(lambda_test)
stack.add(queue_test)

bucket = Bucket(name="test-bucket")
bucket.add_notification_configuration(
Expand All @@ -46,6 +44,8 @@ def test_bucket(stack: Stack) -> None:
event="s3:ObjectCreated:*", target=queue_test, permission_suffix="FileEvent"
)
stack.add(bucket)
stack.add(topic_test)
stack.add(queue_test)

with open(os.path.join(TEST_DIR, "bucket.json")) as fd:
expected_template = json.load(fd)
Expand Down
2 changes: 1 addition & 1 deletion tests/tests_e3_aws/troposphere/sqs/sqs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"Properties": {"QueueName": "myqueue", "VisibilityTimeout": 30},
"Type": "AWS::SQS::Queue",
},
"MyqueuePolicySub": {
"MyqueuePolicy": {
"Properties": {
"PolicyDocument": {
"Statement": [
Expand Down

0 comments on commit 63499e4

Please sign in to comment.