-
Notifications
You must be signed in to change notification settings - Fork 4.1k
fix(events): now EventBus.grantPutEventsTo
correctly handles service principals (under feature flag)
#33729
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This review is outdated)
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #33729 +/- ##
=======================================
Coverage 82.38% 82.38%
=======================================
Files 120 120
Lines 6937 6937
Branches 1170 1170
=======================================
Hits 5715 5715
Misses 1119 1119
Partials 103 103
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
@@ -177,6 +177,21 @@ abstract class EventBusBase extends Resource implements IEventBus { | |||
} | |||
|
|||
public grantPutEventsTo(grantee: iam.IGrantable): iam.Grant { | |||
// Special handling for service principals | |||
if (grantee.grantPrincipal instanceof iam.ServicePrincipal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not a generic solution. We still have issues with other Principle types that does not implement addToPrincipalPolicy
(or it can not linked to a Policy resource), like AccountPrincipal, AnyPrincipal.
EventBus resource supports the ResourcePolicy (AWS::Events::EventBusPolicy) .. so we should make the EventBus L2 to implement the interface IResourceWithPolicy, and to implements addToResourcePolicy
function which looks it is already there.
Then you can change the implementation of this function from iam.Grant.addToPrincipal
to be iam.Grant.addToPrincipalOrResource
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one more thing, I am not sure if it is Ok to create multiple EventBusPolicy resources for the same EventBus or not .. we need to have an integ test case for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks Seb for raising this PR, I left some comment
…ntPutEventsTo * Use Grant.addToPrincipalOrResource for consistent cross-account and service principal handling * Add default SID generation for EventBus resource policies * Add comprehensive test coverage including: - Same account IAM principals - Cross-account service principals - Cross-account IAM principals - Cross-account organization principals - Token account scenarios - Imported event bus cases
…ncipals in EventBus.grantPutEventsTo
EventBus.grantPutEventsTo
correctly handles service principals (under feature flag)
…us resource policies When granting permissions to service principals using grantPutEventsTo(), the operation currently fails silently because service principals require resource policies with Statement IDs. This change introduces a new feature flag '@aws-cdk/aws-events:requireEventBusPolicySid' that controls this behavior: * When enabled (recommended): - Resource policies will be created with Statement IDs for service principals - The operation will succeed as expected * When disabled (legacy): - A warning will be emitted - The grant operation will be dropped - No permissions will be added The feature flag is set to 'true' in recommended-feature-flags.json to ensure proper behavior by default in future versions.
Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBusPolicy', { | ||
Statement: Match.objectLike({ | ||
Effect: 'Allow', | ||
Action: 'events:PutEvents', | ||
Principal: { Service: 'states.amazonaws.com' }, | ||
Resource: Match.anyValue(), | ||
Condition: Match.absent(), | ||
}), | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to understand, what we are checking here different than the check in line 118
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, this is confusing and this 2nd assertion doesn't add much value. I will re-write the assertions with helper functions in order to improve the readability.
import { STANDARD_NODEJS_RUNTIME } from '../../config'; | ||
|
||
// Create a single app for both stacks | ||
const app = new App(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this integ test case, there is no difference between with feature flag, and without. In both cases the permissions will be added to the Role .. may be this test case is designed to verify that the current customer code will stay working. so, can we have a new integ test case to verify the new behaviour of grant method, and also, to verify that we can call grant method more than one in the new approach.
test('Event Bus policy statements must have a sid', () => { | ||
// GIVEN | ||
const app = new App(); | ||
const stack = new Stack(app, 'Stack'); | ||
const bus = new EventBus(stack, 'Bus'); | ||
|
||
// THEN | ||
expect(() => bus.addToResourcePolicy(new iam.PolicyStatement({ | ||
effect: iam.Effect.ALLOW, | ||
principals: [new iam.ArnPrincipal('arn')], | ||
actions: ['events:PutEvents'], | ||
}))).toThrow('Event Bus policy statements must have a sid'); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this test case is still valid, right ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good catch.
}); | ||
}); | ||
|
||
test('grantPutEventsTo creates both IAM and resource policies for cross-account service principal', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the test title is wrong.
expect(grant.success).toBeTruthy(); | ||
}); | ||
|
||
test('grantPutEventsTo creates both IAM and resource policies for cross-account role principal', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one as well, has a wrong title
expect(grant.success).toBeTruthy(); | ||
}); | ||
|
||
test('grantPutEventsTo creates both IAM and resource policies for cross-account organization principal', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here as well
Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); | ||
|
||
// Verify all required parts of the organization policy | ||
Template.fromStack(stack).hasResourceProperties('AWS::Events::EventBusPolicy', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is the difference between this check and check in line 217
…t we can grant to a service principal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Seb, I still have some concern about the new integration test case
/** | ||
* Verifies that no resource policy (EventBusPolicy resource) was created. | ||
*/ | ||
function assertNoResourcePolicy(stack: Stack) { | ||
Template.fromStack(stack).resourceCountIs('AWS::Events::EventBusPolicy', 0); | ||
} | ||
|
||
/** | ||
* Verifies that no identity-based policy was created. | ||
*/ | ||
function assertNoIdentityPolicy(stack: Stack) { | ||
Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit comment, and you can ignore it .. I believe the assert statement itself is clear, and does not need to be wrapped in a function
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, but I prefer the readability of the method name in the test.
expect(grant.success).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('cross-account scenarios', () => { | ||
test('grantPutEventsTo creates EventBusPolicy for service principal without conditions', () => { | ||
test('creates resource policy for service principal without conditions', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this test case is not a cross account test case, and there is no big difference vs test case creates only resource policy for service principal with source account condition
from this change POV.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I will remove it.
}); | ||
|
||
test('grantPutEventsTo creates both IAM and resource policies for cross-account service principal', () => { | ||
test('creates resource policy for service principal with cross-account condition', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I also believe this test case is redundant, I think from this change POV, the Service principal with account condition is not different from a normal service principal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I will remove it as well.
':', | ||
{ Ref: 'AWS::AccountId' }, | ||
':event-bus/', | ||
{ Ref: 'EventBus7B8748AA' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to the previous comment, I expect to see some normal string value, instead of ref intrinsic function.
// GIVEN | ||
// Create a role with a token principal | ||
const role = new iam.Role(stack, 'Role', { | ||
assumedBy: new iam.AccountRootPrincipal(), // This will use the stack's account token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to understand, why do we care about this token .. how may this value change the process of adding the permissions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case was making sense when I was trying to use the Grant.addToPrincipalOrResource(...)
method in EventBusBase.grantPutEventsTo(...)
. Which not the strategy used anymore. I will remove this test.
}); | ||
}); | ||
|
||
describe('cross-stack scenarios', () => { | ||
test('grantPutEventsTo works across stacks', () => { | ||
test('creates identity-based policy in different stack', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test case to grant an imported role that is not managed by the CDK application. I expect in this scenario, we should see an Event Resource Policy instead of identity policy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will add that test case, but the template generated is:
ImportedRoleIdPolicy878A765C :: {
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": "events:PutEvents",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [ "EventBus7B8748AA", "Arn" ],
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "ImportedRoleIdPolicy878A765C",
"Roles": [ "importedRoleName" ]
},
"Type": "AWS::IAM::Policy"
}
Even when I use iam.Role.fromRoleArn(...)
. The code in Grant.addToPrincipal(...) call directly ImportedRole.addToPrincipalPolicy(...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you call fromRolexxx
function, set mutable flag to false .. https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_iam.FromRoleArnOptions.html#mutable
This means that CDK could not attach policies to this Role.
@@ -0,0 +1,142 @@ | |||
import * as cdk from 'aws-cdk-lib'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test case is also miss leading, if you checked the generated template, you will find the following:
TargetEventBuscdkEventsServicePrincipalGrant11A8AC78
Event resource policy that grantevents.amazonaws.com
service to put event on eventBusTargetEventBus43DE6DE2
(this is the point we need to verify)- But also, you will find
ForwardingRuleEventsRoleDefaultPolicy5A4528E2
identity policy that grant roleForwardingRuleEventsRole9F12172A
to put events on EventBusTargetEventBus43DE6DE2
, and this role is attached to EventRuleForwardingRule6039FF0D
which is linked to EventBusSourceEventBusA326BD45
So, I am not sure which policy is applied. .. can you try to change this logic, to add a lambda function that try to put some event to an event bus, and do not grant the function itself, but grant the Lambda Service, and see if this will work or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, it seems that the EventBus
class implementation (link) of the method bind
automatically tries to add the put event policy on its associated role (type iam.IRole
), which is not compatible with the service principal we are using in this integ test.
Since I can't think of a realistic use case where we would only use a service principal role, I will remove this integ test.
…icy case. Add an integ test which uses 2 accounts to force the resource policy scenario.
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
EventBus.grantPutEventsTo
correctly handles service principals (under feature flag)EventBus.grantPutEventsTo
correctly handles service principals (under feature flag)
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Comments on closed issues and PRs are hard for our team to see. |
Issue #22080
Closes #22080.
Reason for this change
When trying to grant PutEvents permissions to an AWS Service Principal using
grantPutEventsTo
, the method performed a no-op without any warnings or errors. This prevented users from properly granting permissions to service principals, even though this is a valid use case that can be done through the AWS Console. The change implements the correct behavior by creating an EventBusPolicy when dealing with service principals.Description of changes
EventBus.grantPutEventsTo
methodiam.Grant.drop()
for service principals to indicate permissions are handled via EventBusPolicyDescribe any new or updated permissions being added
The change introduces the creation of EventBusPolicy resources with
events:PutEvents
permission when granting access to service principals. This is not a new permission, but rather a different way of granting the same permission through resource-based policies instead of identity-based policies.Description of how you validated changes
Added new test cases that verify:
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license