fix: add resourceowner to check for project in project grant (#8785)
Some checks failed
ZITADEL CI/CD / core (push) Waiting to run
ZITADEL CI/CD / console (push) Waiting to run
ZITADEL CI/CD / version (push) Waiting to run
ZITADEL CI/CD / compile (push) Blocked by required conditions
ZITADEL CI/CD / core-unit-test (push) Blocked by required conditions
ZITADEL CI/CD / core-integration-test (push) Blocked by required conditions
ZITADEL CI/CD / lint (push) Blocked by required conditions
ZITADEL CI/CD / container (push) Blocked by required conditions
ZITADEL CI/CD / e2e (push) Blocked by required conditions
ZITADEL CI/CD / release (push) Blocked by required conditions
Code Scanning / CodeQL-Build (javascript) (push) Failing after 7m42s
Code Scanning / CodeQL-Build (go) (push) Failing after 15m0s

# Which Problems Are Solved

Resource owner can be different than expected if the provided
x-zitadel-orgid header is provided.

# How the Problems Are Solved

Check that the project is only checked with the correct resource owner
to avoid unexpected situations.

# Additional Changes

None

# Additional Context

Closes #8685

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz 2024-10-30 09:53:00 +01:00 committed by GitHub
parent cff4fe5dfd
commit 6780c5a07c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 114 additions and 9 deletions

View File

@ -27,7 +27,7 @@ func (c *Commands) AddProjectGrant(ctx context.Context, grant *domain.ProjectGra
if !grant.IsValid() {
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-3b8fs", "Errors.Project.Grant.Invalid")
}
err = c.checkProjectGrantPreCondition(ctx, grant)
err = c.checkProjectGrantPreCondition(ctx, grant, resourceOwner)
if err != nil {
return nil, err
}
@ -67,7 +67,7 @@ func (c *Commands) ChangeProjectGrant(ctx context.Context, grant *domain.Project
return nil, err
}
grant.GrantedOrgID = existingGrant.GrantedOrgID
err = c.checkProjectGrantPreCondition(ctx, grant)
err = c.checkProjectGrantPreCondition(ctx, grant, resourceOwner)
if err != nil {
return nil, err
}
@ -255,11 +255,11 @@ func (c *Commands) projectGrantWriteModelByID(ctx context.Context, grantID, proj
return writeModel, nil
}
func (c *Commands) checkProjectGrantPreCondition(ctx context.Context, projectGrant *domain.ProjectGrant) error {
func (c *Commands) checkProjectGrantPreCondition(ctx context.Context, projectGrant *domain.ProjectGrant, resourceOwner string) error {
if !authz.GetFeatures(ctx).ShouldUseImprovedPerformance(feature.ImprovedPerformanceTypeProjectGrant) {
return c.checkProjectGrantPreConditionOld(ctx, projectGrant)
return c.checkProjectGrantPreConditionOld(ctx, projectGrant, resourceOwner)
}
existingRoleKeys, err := c.searchProjectGrantState(ctx, projectGrant.AggregateID, projectGrant.GrantedOrgID)
existingRoleKeys, err := c.searchProjectGrantState(ctx, projectGrant.AggregateID, projectGrant.GrantedOrgID, resourceOwner)
if err != nil {
return err
}
@ -270,11 +270,12 @@ func (c *Commands) checkProjectGrantPreCondition(ctx context.Context, projectGra
return nil
}
func (c *Commands) searchProjectGrantState(ctx context.Context, projectID, grantedOrgID string) (existingRoleKeys []string, err error) {
func (c *Commands) searchProjectGrantState(ctx context.Context, projectID, grantedOrgID, resourceOwner string) (existingRoleKeys []string, err error) {
results, err := c.eventstore.Search(
ctx,
// project state query
map[eventstore.FieldType]any{
eventstore.FieldTypeResourceOwner: resourceOwner,
eventstore.FieldTypeAggregateType: project.AggregateType,
eventstore.FieldTypeAggregateID: projectID,
eventstore.FieldTypeFieldName: project.ProjectStateSearchField,
@ -289,6 +290,7 @@ func (c *Commands) searchProjectGrantState(ctx context.Context, projectID, grant
},
// role query
map[eventstore.FieldType]any{
eventstore.FieldTypeResourceOwner: resourceOwner,
eventstore.FieldTypeAggregateType: project.AggregateType,
eventstore.FieldTypeAggregateID: projectID,
eventstore.FieldTypeFieldName: project.ProjectRoleKeySearchField,

View File

@ -121,8 +121,9 @@ type ProjectGrantPreConditionReadModel struct {
ExistingRoleKeys []string
}
func NewProjectGrantPreConditionReadModel(projectID, grantedOrgID string) *ProjectGrantPreConditionReadModel {
func NewProjectGrantPreConditionReadModel(projectID, grantedOrgID, resourceOwner string) *ProjectGrantPreConditionReadModel {
return &ProjectGrantPreConditionReadModel{
WriteModel: eventstore.WriteModel{ResourceOwner: resourceOwner},
ProjectID: projectID,
GrantedOrgID: grantedOrgID,
}
@ -132,12 +133,24 @@ func (wm *ProjectGrantPreConditionReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *project.ProjectAddedEvent:
if e.Aggregate().ResourceOwner != wm.ResourceOwner {
continue
}
wm.ProjectExists = true
case *project.ProjectRemovedEvent:
if e.Aggregate().ResourceOwner != wm.ResourceOwner {
continue
}
wm.ProjectExists = false
case *project.RoleAddedEvent:
if e.Aggregate().ResourceOwner != wm.ResourceOwner {
continue
}
wm.ExistingRoleKeys = append(wm.ExistingRoleKeys, e.Key)
case *project.RoleRemovedEvent:
if e.Aggregate().ResourceOwner != wm.ResourceOwner {
continue
}
for i, key := range wm.ExistingRoleKeys {
if key == e.Key {
copy(wm.ExistingRoleKeys[i:], wm.ExistingRoleKeys[i+1:])

View File

@ -79,6 +79,50 @@ func TestCommandSide_AddProjectGrant(t *testing.T) {
err: zerrors.IsPreconditionFailed,
},
},
{
name: "project not existing in org, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "otherorg").Aggregate,
"projectname1", true, true, true,
domain.PrivateLabelingSettingUnspecified,
),
),
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("grantedorg1").Aggregate,
"granted org",
),
),
eventFromEventPusher(
project.NewRoleAddedEvent(context.Background(),
&project.NewAggregate("project1", "otherorg").Aggregate,
"key1",
"key",
"",
),
),
),
),
},
args: args{
ctx: context.Background(),
projectGrant: &domain.ProjectGrant{
ObjectRoot: models.ObjectRoot{
AggregateID: "project1",
},
GrantedOrgID: "grantedorg1",
},
resourceOwner: "org1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "granted org not existing, precondition error",
fields: fields{
@ -325,6 +369,52 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
err: zerrors.IsPreconditionFailed,
},
},
{
name: "project not existing in org, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(project.NewGrantAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"projectgrant1",
"grantedorg1",
[]string{"key1"},
)),
),
expectFilter(
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "otherorg").Aggregate,
"projectname1", true, true, true,
domain.PrivateLabelingSettingUnspecified,
),
),
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("grantedorg1").Aggregate,
"granted org",
),
),
),
),
},
args: args{
ctx: context.Background(),
projectGrant: &domain.ProjectGrant{
ObjectRoot: models.ObjectRoot{
AggregateID: "project1",
},
GrantID: "projectgrant1",
GrantedOrgID: "grantedorg1",
RoleKeys: []string{"key1"},
},
resourceOwner: "org1",
},
res: res{
err: zerrors.IsPreconditionFailed,
},
},
{
name: "granted org not existing, precondition error",
fields: fields{

View File

@ -161,8 +161,8 @@ func (c *Commands) removeProjectOld(ctx context.Context, projectID, resourceOwne
return writeModelToObjectDetails(&existingProject.WriteModel), nil
}
func (c *Commands) checkProjectGrantPreConditionOld(ctx context.Context, projectGrant *domain.ProjectGrant) error {
preConditions := NewProjectGrantPreConditionReadModel(projectGrant.AggregateID, projectGrant.GrantedOrgID)
func (c *Commands) checkProjectGrantPreConditionOld(ctx context.Context, projectGrant *domain.ProjectGrant, resourceOwner string) error {
preConditions := NewProjectGrantPreConditionReadModel(projectGrant.AggregateID, projectGrant.GrantedOrgID, resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, preConditions)
if err != nil {
return err