fix: generated project grant id (#10747)

# Which Problems Are Solved

Project Grant ID would have needed to be unique to be handled properly
on the projections, but was defined as the organization ID the project
was granted to, so could be non-unique.

# How the Problems Are Solved

Generate the Project Grant ID even in the v2 APIs, which also includes
fixes in the integration tests.
Additionally to that, the logic for some functionality had to be
extended as the Project Grant ID is not provided anymore in the API, so
had to be queried before creating events for Project Grants.

# Additional Changes

Included fix for authorizations, when an authorization was intended to
be created for a project, without providing any organization
information, which also showed some faulty integration tests.

# Additional Context

Partially closes #10745

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
This commit is contained in:
Stefan Benz
2025-09-21 16:04:25 +02:00
committed by GitHub
parent adc8fb2013
commit b6ff4ff16c
19 changed files with 465 additions and 194 deletions

View File

@@ -263,9 +263,9 @@ func TestServer_ListAuthorizations(t *testing.T) {
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, integration.Email())
resp := createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectGrantId{
ProjectGrantId: &filter.IDFilter{
Id: resp.GetProjectGrantId(),
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_OrganizationId{
OrganizationId: &filter.IDFilter{
Id: resp.GetGrantedOrganizationId(),
},
}
response.Authorizations[0] = resp
@@ -330,8 +330,8 @@ func TestServer_ListAuthorizations(t *testing.T) {
},
}
response.Authorizations[1] = createAuthorizationForProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
response.Authorizations[0] = createAuthorizationWithProjectGrant(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
response.Authorizations[0] = createAuthorizationForProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
createAuthorizationWithProjectGrant(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
},
req: &authorization.ListAuthorizationsRequest{
Filters: []*authorization.AuthorizationsSearchFilter{{}},
@@ -343,7 +343,7 @@ func TestServer_ListAuthorizations(t *testing.T) {
AppliedLimit: 100,
},
Authorizations: []*authorization.Authorization{
{}, {},
{},
},
},
},
@@ -426,6 +426,10 @@ func TestServer_ListAuthorizations(t *testing.T) {
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Authorizations, len(tt.want.Authorizations)) {
for i := range tt.want.Authorizations {
// set as project grant id is generated
if grant := got.Authorizations[i].ProjectGrantId; grant != nil {
tt.want.Authorizations[i].ProjectGrantId = grant
}
assert.EqualExportedValues(ttt, tt.want.Authorizations[i], got.Authorizations[i])
}
}
@@ -454,15 +458,15 @@ func createAuthorizationForProject(ctx context.Context, instance *integration.In
userResp, err := instance.Client.UserV2.GetUserByID(ctx, &user.GetUserByIDRequest{UserId: userID})
require.NoError(t, err)
userGrantResp := instance.CreateProjectUserGrant(t, ctx, orgID, projectID, userID)
authResp := instance.CreateAuthorizationProject(t, ctx, projectID, userID)
return &authorization.Authorization{
Id: userGrantResp.GetUserGrantId(),
Id: authResp.GetId(),
ProjectId: projectID,
ProjectName: projectName,
ProjectOrganizationId: orgID,
OrganizationId: orgID,
CreationDate: userGrantResp.Details.GetCreationDate(),
ChangeDate: userGrantResp.Details.GetCreationDate(),
CreationDate: authResp.GetCreationDate(),
ChangeDate: authResp.GetCreationDate(),
State: 1,
User: &authorization.User{
Id: userID,
@@ -486,17 +490,18 @@ func createAuthorizationForProjectGrant(ctx context.Context, instance *integrati
userResp, err := instance.Client.UserV2.GetUserByID(ctx, &user.GetUserByIDRequest{UserId: userID})
require.NoError(t, err)
userGrantResp := instance.CreateProjectGrantUserGrant(ctx, orgID, projectID, grantedOrgID, userID)
authResp := instance.CreateAuthorizationProjectGrant(t, ctx, projectID, grantedOrgID, userID)
return &authorization.Authorization{
Id: userGrantResp.GetUserGrantId(),
Id: authResp.GetId(),
ProjectId: projectID,
ProjectName: projectName,
ProjectOrganizationId: orgID,
ProjectGrantId: gu.Ptr(grantedOrgID),
// empty as generated
ProjectGrantId: nil,
GrantedOrganizationId: gu.Ptr(grantedOrgID),
OrganizationId: orgID,
CreationDate: userGrantResp.Details.GetCreationDate(),
ChangeDate: userGrantResp.Details.GetCreationDate(),
OrganizationId: grantedOrgID,
CreationDate: authResp.GetCreationDate(),
ChangeDate: authResp.GetCreationDate(),
State: 1,
User: &authorization.User{
Id: userID,
@@ -795,9 +800,9 @@ func TestServer_ListAuthorizations_PermissionsV2(t *testing.T) {
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, integration.Email())
resp := createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectGrantId{
ProjectGrantId: &filter.IDFilter{
Id: resp.GetProjectGrantId(),
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_OrganizationId{
OrganizationId: &filter.IDFilter{
Id: resp.GetGrantedOrganizationId(),
},
}
response.Authorizations[0] = resp
@@ -862,8 +867,8 @@ func TestServer_ListAuthorizations_PermissionsV2(t *testing.T) {
},
}
response.Authorizations[1] = createAuthorizationForProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
response.Authorizations[0] = createAuthorizationWithProjectGrant(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
response.Authorizations[0] = createAuthorizationForProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
createAuthorizationWithProjectGrant(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
},
req: &authorization.ListAuthorizationsRequest{
Filters: []*authorization.AuthorizationsSearchFilter{{}},
@@ -875,7 +880,7 @@ func TestServer_ListAuthorizations_PermissionsV2(t *testing.T) {
AppliedLimit: 100,
},
Authorizations: []*authorization.Authorization{
{}, {},
{},
},
},
},
@@ -958,6 +963,10 @@ func TestServer_ListAuthorizations_PermissionsV2(t *testing.T) {
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Authorizations, len(tt.want.Authorizations)) {
for i := range tt.want.Authorizations {
// set as project grant id is generated
if grant := got.Authorizations[i].ProjectGrantId; grant != nil {
tt.want.Authorizations[i].ProjectGrantId = grant
}
assert.EqualExportedValues(ttt, tt.want.Authorizations[i], got.Authorizations[i])
}
}

View File

@@ -85,10 +85,10 @@ func createAdministratorProjectToCommand(req *internal_permission.ResourceType_P
func createAdministratorProjectGrantToCommand(req *internal_permission.ResourceType_ProjectGrant_, userID string, roles []string) *command.AddProjectGrantMember {
return &command.AddProjectGrantMember{
GrantID: req.ProjectGrant.ProjectGrantId,
ProjectID: req.ProjectGrant.ProjectId,
UserID: userID,
Roles: roles,
OrganizationID: req.ProjectGrant.OrganizationId,
ProjectID: req.ProjectGrant.ProjectId,
UserID: userID,
Roles: roles,
}
}
@@ -165,10 +165,10 @@ func updateAdministratorProjectToCommand(req *internal_permission.ResourceType_P
func updateAdministratorProjectGrantToCommand(req *internal_permission.ResourceType_ProjectGrant_, userID string, roles []string) *command.ChangeProjectGrantMember {
return &command.ChangeProjectGrantMember{
GrantID: req.ProjectGrant.ProjectGrantId,
ProjectID: req.ProjectGrant.ProjectId,
UserID: userID,
Roles: roles,
OrganizationID: req.ProjectGrant.OrganizationId,
ProjectID: req.ProjectGrant.ProjectId,
UserID: userID,
Roles: roles,
}
}
@@ -203,7 +203,7 @@ func (s *Server) DeleteAdministrator(ctx context.Context, req *connect.Request[i
deletionDate = timestamppb.New(member.EventDate)
}
case *internal_permission.ResourceType_ProjectGrant_:
member, err := s.command.RemoveProjectGrantMember(ctx, resource.ProjectGrant.ProjectId, req.Msg.UserId, resource.ProjectGrant.ProjectGrantId)
member, err := s.command.RemoveProjectGrantMember(ctx, resource.ProjectGrant.ProjectId, req.Msg.UserId, "", resource.ProjectGrant.OrganizationId)
if err != nil {
return nil, err
}

View File

@@ -239,7 +239,7 @@ func TestServer_CreateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: "notexisting",
OrganizationId: "notexisting",
},
},
}
@@ -263,7 +263,7 @@ func TestServer_CreateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -289,7 +289,7 @@ func TestServer_CreateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -334,6 +334,13 @@ func TestServer_CreateAdministrator_Permission(t *testing.T) {
patProjectGrantResp := instance.CreatePersonalAccessToken(iamOwnerCtx, userProjectGrantResp.GetUserId())
projectGrantOwnerCtx := integration.WithAuthorizationToken(CTX, patProjectGrantResp.Token)
grantedProjectResp := instance.CreateProject(iamOwnerCtx, t, orgResp.GetOrganizationId(), integration.ProjectName(), false, false)
userGrantedProjectResp := instance.CreateMachineUser(iamOwnerCtx)
instance.CreateProjectMembership(t, iamOwnerCtx, grantedProjectResp.GetId(), userGrantedProjectResp.GetUserId())
patGrantedProjectResp := instance.CreatePersonalAccessToken(iamOwnerCtx, userGrantedProjectResp.GetUserId())
grantedProjectOwnerCtx := integration.WithAuthorizationToken(CTX, patGrantedProjectResp.Token)
instance.CreateProjectGrant(iamOwnerCtx, t, grantedProjectResp.GetId(), instance.DefaultOrg.GetId())
type want struct {
creationDate bool
}
@@ -508,6 +515,50 @@ func TestServer_CreateAdministrator_Permission(t *testing.T) {
},
wantErr: true,
},
{
name: "project grant, org owner, ok",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
prepare: func(request *internal_permission.CreateAdministratorRequest) {
userResp := instance.CreateUserTypeHuman(iamOwnerCtx, integration.Email())
request.UserId = userResp.GetId()
},
req: &internal_permission.CreateAdministratorRequest{
Resource: &internal_permission.ResourceType{
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: grantedProjectResp.GetId(),
OrganizationId: instance.DefaultOrg.GetId(),
},
},
},
Roles: []string{"PROJECT_GRANT_OWNER"},
},
want: want{
creationDate: true,
},
},
{
name: "project grant, project owner, error",
ctx: grantedProjectOwnerCtx,
prepare: func(request *internal_permission.CreateAdministratorRequest) {
userResp := instance.CreateUserTypeHuman(iamOwnerCtx, integration.Email())
request.UserId = userResp.GetId()
},
req: &internal_permission.CreateAdministratorRequest{
Resource: &internal_permission.ResourceType{
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: grantedProjectResp.GetId(),
OrganizationId: instance.DefaultOrg.GetId(),
},
},
},
Roles: []string{"PROJECT_GRANT_OWNER"},
},
wantErr: true,
},
{
name: "project grant, project grant owner, ok",
ctx: projectGrantOwnerCtx,
@@ -521,7 +572,7 @@ func TestServer_CreateAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -544,7 +595,7 @@ func TestServer_CreateAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -869,7 +920,7 @@ func TestServer_UpdateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: "notexisting",
OrganizationId: "notexisting",
},
},
}
@@ -894,7 +945,7 @@ func TestServer_UpdateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -922,7 +973,7 @@ func TestServer_UpdateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -949,7 +1000,7 @@ func TestServer_UpdateAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -1192,7 +1243,7 @@ func TestServer_UpdateAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -1215,7 +1266,7 @@ func TestServer_UpdateAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -1236,7 +1287,7 @@ func TestServer_UpdateAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -1487,7 +1538,7 @@ func TestServer_DeleteAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: "notexisting",
OrganizationId: "notexisting",
},
},
}
@@ -1512,7 +1563,7 @@ func TestServer_DeleteAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -1538,7 +1589,7 @@ func TestServer_DeleteAdministrator(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
}
@@ -1766,7 +1817,7 @@ func TestServer_DeleteAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -1788,7 +1839,7 @@ func TestServer_DeleteAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},
@@ -1808,7 +1859,7 @@ func TestServer_DeleteAdministrator_Permission(t *testing.T) {
Resource: &internal_permission.ResourceType_ProjectGrant_{
ProjectGrant: &internal_permission.ResourceType_ProjectGrant{
ProjectId: projectResp.GetId(),
ProjectGrantId: orgResp.GetOrganizationId(),
OrganizationId: orgResp.GetOrganizationId(),
},
},
},

View File

@@ -535,6 +535,10 @@ func TestServer_ListAdministrators(t *testing.T) {
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Administrators, len(tt.want.Administrators)) {
for i := range tt.want.Administrators {
// need to set the project grant ID as it is generated
if grant := got.Administrators[i].GetProjectGrant(); grant != nil {
tt.want.Administrators[i].GetProjectGrant().Id = grant.Id
}
assert.EqualExportedValues(ttt, tt.want.Administrators[i], got.Administrators[i])
}
}
@@ -631,7 +635,8 @@ func createProjectGrantAdministrator(ctx context.Context, instance *integration.
},
Resource: &internal_permission.Administrator_ProjectGrant{
ProjectGrant: &internal_permission.ProjectGrant{
Id: grantedOrgID,
// left empty as generated
Id: "",
ProjectId: projectID,
ProjectName: projectName,
OrganizationId: orgID,
@@ -1162,6 +1167,10 @@ func TestServer_ListAdministrators_PermissionV2(t *testing.T) {
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Administrators, len(tt.want.Administrators)) {
for i := range tt.want.Administrators {
// set as project grant id is generated
if grant := got.Administrators[i].GetProjectGrant(); grant != nil {
tt.want.Administrators[i].GetProjectGrant().Id = grant.Id
}
assert.EqualExportedValues(ttt, tt.want.Administrators[i], got.Administrators[i])
}
}

View File

@@ -204,7 +204,7 @@ func (s *Server) UpdateProjectGrantMember(ctx context.Context, req *mgmt_pb.Upda
}
func (s *Server) RemoveProjectGrantMember(ctx context.Context, req *mgmt_pb.RemoveProjectGrantMemberRequest) (*mgmt_pb.RemoveProjectGrantMemberResponse, error) {
details, err := s.command.RemoveProjectGrantMember(ctx, req.ProjectId, req.UserId, req.GrantId)
details, err := s.command.RemoveProjectGrantMember(ctx, req.ProjectId, req.UserId, req.GrantId, "")
if err != nil {
return nil, err
}

View File

@@ -147,19 +147,19 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
func AddProjectGrantMemberRequestToCommand(req *mgmt_pb.AddProjectGrantMemberRequest, orgID string) *command.AddProjectGrantMember {
return &command.AddProjectGrantMember{
ResourceOwner: orgID,
ProjectID: req.ProjectId,
GrantID: req.GrantId,
UserID: req.UserId,
Roles: req.Roles,
ResourceOwner: orgID,
ProjectID: req.ProjectId,
ProjectGrantID: req.GrantId,
UserID: req.UserId,
Roles: req.Roles,
}
}
func UpdateProjectGrantMemberRequestToCommand(req *mgmt_pb.UpdateProjectGrantMemberRequest) *command.ChangeProjectGrantMember {
return &command.ChangeProjectGrantMember{
ProjectID: req.ProjectId,
GrantID: req.GrantId,
UserID: req.UserId,
Roles: req.Roles,
ProjectID: req.ProjectId,
ProjectGrantID: req.GrantId,
UserID: req.UserId,
Roles: req.Roles,
}
}

View File

@@ -426,7 +426,7 @@ func TestServer_CreateCallback_Permission(t *testing.T) {
orgResp := Instance.CreateOrganization(ctx, integration.OrganizationName(), integration.Email())
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, Instance.DefaultOrg.GetId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndAuthRequestForCallback(ctx, t, clientID, Instance.Users.Get(integration.UserTypeLogin).ID, user.GetUserId())
},
@@ -564,7 +564,7 @@ func TestServer_CreateCallback_Permission(t *testing.T) {
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, orgResp.GetOrganizationId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndAuthRequestForCallback(ctx, t, clientID, Instance.Users.Get(integration.UserTypeLogin).ID, user.GetUserId())
},
want: &oidc_pb.CreateCallbackResponse{
@@ -940,26 +940,26 @@ func createOIDCApplication(ctx context.Context, t *testing.T, projectRoleCheck,
}
func createProjectUserGrant(ctx context.Context, t *testing.T, orgID, projectID, userID string) {
resp := Instance.CreateProjectUserGrant(t, ctx, orgID, projectID, userID)
resp := Instance.CreateAuthorizationProject(t, ctx, projectID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)
}
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, orgID, projectID, projectGrantID, userID string) {
resp := Instance.CreateProjectGrantUserGrant(ctx, orgID, projectID, projectGrantID, userID)
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, projectID, grantedOrgID, userID string) {
resp := Instance.CreateAuthorizationProjectGrant(t, ctx, projectID, grantedOrgID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, grantedOrgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)

View File

@@ -427,7 +427,7 @@ func TestServer_CreateCallback_Permission(t *testing.T) {
orgResp := Instance.CreateOrganization(ctx, integration.OrganizationName(), integration.Email())
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, orgResp.GetOrganizationId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndAuthRequestForCallback(ctx, t, clientID, Instance.Users.Get(integration.UserTypeLogin).ID, user.GetUserId())
},
@@ -564,7 +564,7 @@ func TestServer_CreateCallback_Permission(t *testing.T) {
orgResp := Instance.CreateOrganization(ctx, integration.OrganizationName(), integration.Email())
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, orgResp.GetOrganizationId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndAuthRequestForCallback(ctx, t, clientID, Instance.Users.Get(integration.UserTypeLogin).ID, user.GetUserId())
},
want: &oidc_pb.CreateCallbackResponse{
@@ -693,26 +693,26 @@ func createOIDCApplication(ctx context.Context, t *testing.T, projectRoleCheck,
}
func createProjectUserGrant(ctx context.Context, t *testing.T, orgID, projectID, userID string) {
resp := Instance.CreateProjectUserGrant(t, ctx, orgID, projectID, userID)
resp := Instance.CreateAuthorizationProject(t, ctx, projectID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)
}
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, orgID, projectID, projectGrantID, userID string) {
resp := Instance.CreateProjectGrantUserGrant(ctx, orgID, projectID, projectGrantID, userID)
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, projectID, grantedOrgID, userID string) {
resp := Instance.CreateAuthorizationProjectGrant(t, ctx, projectID, grantedOrgID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, grantedOrgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)

View File

@@ -32,7 +32,6 @@ func projectGrantCreateToCommand(req *project_pb.CreateProjectGrantRequest) *com
ObjectRoot: models.ObjectRoot{
AggregateID: req.ProjectId,
},
GrantID: req.GrantedOrganizationId,
GrantedOrgID: req.GrantedOrganizationId,
RoleKeys: req.RoleKeys,
}
@@ -113,7 +112,7 @@ func (s *Server) userGrantsFromProjectGrant(ctx context.Context, projectID, gran
if err != nil {
return nil, err
}
grantQuery, err := query.NewUserGrantGrantIDSearchQuery(grantedOrganizationID)
grantQuery, err := query.NewUserGrantWithGrantedQuery(grantedOrganizationID)
if err != nil {
return nil, err
}

View File

@@ -416,7 +416,7 @@ func TestServer_CreateResponse_Permission(t *testing.T) {
orgResp := Instance.CreateOrganization(ctx, integration.OrganizationName(), integration.Email())
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, orgResp.GetOrganizationId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndSmlRequestForCallback(ctx, t, sp, Instance.Users[integration.UserTypeLogin].ID, acsRedirect, user.GetUserId(), saml.HTTPRedirectBinding)
},
@@ -550,7 +550,7 @@ func TestServer_CreateResponse_Permission(t *testing.T) {
orgResp := Instance.CreateOrganization(ctx, integration.OrganizationName(), integration.Email())
Instance.CreateProjectGrant(ctx, t, projectID, orgResp.GetOrganizationId())
user := Instance.CreateHumanUserVerified(ctx, orgResp.GetOrganizationId(), integration.Email(), integration.Phone())
createProjectGrantUserGrant(ctx, t, orgResp.GetOrganizationId(), projectID, orgResp.GetOrganizationId(), user.GetUserId())
createProjectGrantUserGrant(ctx, t, projectID, orgResp.GetOrganizationId(), user.GetUserId())
return createSessionAndSmlRequestForCallback(ctx, t, sp, Instance.Users[integration.UserTypeLogin].ID, acsRedirect, user.GetUserId(), saml.HTTPRedirectBinding)
},
@@ -694,26 +694,26 @@ func createSAMLApplication(ctx context.Context, t *testing.T, idpMetadata *saml.
}
func createProjectUserGrant(ctx context.Context, t *testing.T, orgID, projectID, userID string) {
resp := Instance.CreateProjectUserGrant(t, ctx, orgID, projectID, userID)
resp := Instance.CreateAuthorizationProject(t, ctx, projectID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)
}
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, orgID, projectID, projectGrantID, userID string) {
resp := Instance.CreateProjectGrantUserGrant(ctx, orgID, projectID, projectGrantID, userID)
func createProjectGrantUserGrant(ctx context.Context, t *testing.T, projectID, grantedOrgID, userID string) {
resp := Instance.CreateAuthorizationProjectGrant(t, ctx, projectID, grantedOrgID, userID)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
require.EventuallyWithT(t, func(collect *assert.CollectT) {
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, orgID), &mgmt.GetUserGrantByIDRequest{
_, err := Instance.Client.Mgmt.GetUserGrantByID(integration.SetOrgID(ctx, grantedOrgID), &mgmt.GetUserGrantByIDRequest{
UserId: userID,
GrantId: resp.GetUserGrantId(),
GrantId: resp.GetId(),
})
assert.NoError(collect, err)
}, retryDuration, tick)

View File

@@ -133,19 +133,11 @@ func (c *Commands) checkPermissionDeleteProjectMember(ctx context.Context, resou
}
func (c *Commands) checkPermissionUpdateProjectGrantMember(ctx context.Context, grantedOrgID, projectGrantID string) (err error) {
// TODO: add permission check for project grant owners
//if err := c.newPermissionCheck(ctx, domain.PermissionProjectGrantMemberWrite, project.AggregateType)(resourceOwner, projectGrantID); err != nil {
return c.newPermissionCheck(ctx, domain.PermissionProjectGrantMemberWrite, project.AggregateType)(grantedOrgID, projectGrantID)
//}
//return nil
}
func (c *Commands) checkPermissionDeleteProjectGrantMember(ctx context.Context, grantedOrgID, projectGrantID string) (err error) {
// TODO: add permission check for project grant owners
//if err := c.newPermissionCheck(ctx, domain.PermissionProjectGrantMemberDelete, project.AggregateType)(resourceOwner, projectGrantID); err != nil {
return c.newPermissionCheck(ctx, domain.PermissionProjectGrantMemberDelete, project.AggregateType)(grantedOrgID, projectGrantID)
//}
//return nil
}
func (c *Commands) newUserGrantPermissionCheck(ctx context.Context, permission string) UserGrantPermissionCheck {

View File

@@ -234,15 +234,15 @@ func (c *Commands) DeactivateProjectGrant(ctx context.Context, projectID, grantI
return writeModelToObjectDetails(&existingGrant.WriteModel), nil
}
func (c *Commands) checkProjectGrantExists(ctx context.Context, grantID, grantedOrgID, projectID, resourceOwner string) (string, string, error) {
func (c *Commands) checkProjectGrantExists(ctx context.Context, grantID, grantedOrgID, projectID, resourceOwner string) (string, string, string, error) {
existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, grantedOrgID, projectID, resourceOwner)
if err != nil {
return "", "", err
return "", "", "", err
}
if !existingGrant.State.Exists() {
return "", "", zerrors.ThrowNotFound(nil, "PROJECT-D8JxR", "Errors.Project.Grant.NotFound")
return "", "", "", zerrors.ThrowNotFound(nil, "PROJECT-D8JxR", "Errors.Project.Grant.NotFound")
}
return existingGrant.GrantedOrgID, existingGrant.ResourceOwner, nil
return existingGrant.FoundGrantID, existingGrant.GrantedOrgID, existingGrant.ResourceOwner, nil
}
func (c *Commands) ReactivateProjectGrant(ctx context.Context, projectID, grantID, grantedOrgID, resourceOwner string) (details *domain.ObjectDetails, err error) {

View File

@@ -13,15 +13,16 @@ import (
)
type AddProjectGrantMember struct {
ResourceOwner string
UserID string
GrantID string
ProjectID string
Roles []string
ResourceOwner string
UserID string
OrganizationID string
ProjectGrantID string
ProjectID string
Roles []string
}
func (i *AddProjectGrantMember) IsValid(zitadelRoles []authz.RoleMapping) error {
if i.ProjectID == "" || i.GrantID == "" || i.UserID == "" || len(i.Roles) == 0 {
if i.ProjectID == "" || (i.OrganizationID == "" && i.ProjectGrantID == "") || i.UserID == "" || len(i.Roles) == 0 {
return zerrors.ThrowInvalidArgument(nil, "PROJECT-8fi7G", "Errors.Project.Grant.Member.Invalid")
}
if len(domain.CheckForInvalidRoles(i.Roles, domain.ProjectGrantRolePrefix, zitadelRoles)) > 0 {
@@ -41,14 +42,18 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *AddProject
if err != nil {
return nil, err
}
grantedOrgID, projectGrantResourceOwner, err := c.checkProjectGrantExists(ctx, member.GrantID, "", member.ProjectID, "")
projectGrantID, grantedOrgID, projectGrantResourceOwner, err := c.checkProjectGrantExists(ctx, member.ProjectGrantID, member.OrganizationID, member.ProjectID, "")
if err != nil {
return nil, err
}
if member.ResourceOwner == "" {
member.ResourceOwner = projectGrantResourceOwner
}
addedMember, err := c.projectGrantMemberWriteModelByID(ctx, member.ProjectID, member.UserID, member.GrantID, member.ResourceOwner)
// if projectGrantID is not provided, organizationID has to be provided, but we still need the projectGrantID which we query
if member.ProjectGrantID == "" {
member.ProjectGrantID = projectGrantID
}
addedMember, err := c.projectGrantMemberWriteModelByID(ctx, member.ProjectID, member.UserID, member.ProjectGrantID, member.ResourceOwner)
if err != nil {
return nil, err
}
@@ -69,7 +74,7 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *AddProject
project.NewProjectGrantMemberAddedEvent(ctx,
ProjectAggregateFromWriteModelWithCTX(ctx, &addedMember.WriteModel),
member.UserID,
member.GrantID,
member.ProjectGrantID,
member.Roles...,
))
if err != nil {
@@ -84,14 +89,15 @@ func (c *Commands) AddProjectGrantMember(ctx context.Context, member *AddProject
}
type ChangeProjectGrantMember struct {
UserID string
GrantID string
ProjectID string
Roles []string
UserID string
ProjectGrantID string
OrganizationID string
ProjectID string
Roles []string
}
func (i *ChangeProjectGrantMember) IsValid(zitadelRoles []authz.RoleMapping) error {
if i.ProjectID == "" || i.GrantID == "" || i.UserID == "" || len(i.Roles) == 0 {
if i.ProjectID == "" || (i.ProjectGrantID == "" && i.OrganizationID == "") || i.UserID == "" || len(i.Roles) == 0 {
return zerrors.ThrowInvalidArgument(nil, "PROJECT-109fs", "Errors.Project.Grant.Member.Invalid")
}
if len(domain.CheckForInvalidRoles(i.Roles, domain.ProjectGrantRolePrefix, zitadelRoles)) > 0 {
@@ -105,11 +111,14 @@ func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *ChangeP
if err := member.IsValid(c.zitadelRoles); err != nil {
return nil, err
}
existingGrant, err := c.projectGrantWriteModelByID(ctx, member.GrantID, "", member.ProjectID, "")
existingGrant, err := c.projectGrantWriteModelByID(ctx, member.ProjectGrantID, member.OrganizationID, member.ProjectID, "")
if err != nil {
return nil, err
}
existingMember, err := c.projectGrantMemberWriteModelByID(ctx, member.ProjectID, member.UserID, member.GrantID, existingGrant.ResourceOwner)
if member.ProjectGrantID == "" {
member.ProjectGrantID = existingGrant.GrantID
}
existingMember, err := c.projectGrantMemberWriteModelByID(ctx, member.ProjectID, member.UserID, member.ProjectGrantID, existingGrant.ResourceOwner)
if err != nil {
return nil, err
}
@@ -129,7 +138,7 @@ func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *ChangeP
project.NewProjectGrantMemberChangedEvent(ctx,
ProjectAggregateFromWriteModelWithCTX(ctx, &existingMember.WriteModel),
member.UserID,
member.GrantID,
member.ProjectGrantID,
member.Roles...,
))
if err != nil {
@@ -143,15 +152,16 @@ func (c *Commands) ChangeProjectGrantMember(ctx context.Context, member *ChangeP
return writeModelToObjectDetails(&existingMember.WriteModel), nil
}
func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, userID, grantID string) (*domain.ObjectDetails, error) {
if projectID == "" || userID == "" || grantID == "" {
func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, userID, grantID, organizationID string) (*domain.ObjectDetails, error) {
if projectID == "" || userID == "" || (grantID == "" && organizationID == "") {
return nil, zerrors.ThrowInvalidArgument(nil, "PROJECT-66mHd", "Errors.Project.Member.Invalid")
}
existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, "", projectID, "")
existingGrant, err := c.projectGrantWriteModelByID(ctx, grantID, organizationID, projectID, "")
if err != nil {
return nil, err
}
existingMember, err := c.projectGrantMemberWriteModelByID(ctx, projectID, userID, grantID, existingGrant.ResourceOwner)
existingMember, err := c.projectGrantMemberWriteModelByID(ctx, existingGrant.AggregateID, userID, existingGrant.FoundGrantID, existingGrant.ResourceOwner)
if err != nil {
return nil, err
}
@@ -165,7 +175,7 @@ func (c *Commands) RemoveProjectGrantMember(ctx context.Context, projectID, user
removeEvent := c.removeProjectGrantMember(ctx,
ProjectAggregateFromWriteModelWithCTX(ctx, &existingMember.WriteModel),
userID,
grantID,
existingGrant.FoundGrantID,
false,
)
pushedEvents, err := c.eventstore.Push(ctx, removeEvent)

View File

@@ -57,10 +57,10 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
},
args: args{
member: &AddProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -82,10 +82,10 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
},
args: args{
member: &AddProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -142,10 +142,10 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
},
args: args{
member: &AddProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -202,10 +202,10 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
},
args: args{
member: &AddProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -257,10 +257,10 @@ func TestCommandSide_AddProjectGrantMember(t *testing.T) {
},
args: args{
member: &AddProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -331,10 +331,10 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) {
},
args: args{
member: &ChangeProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_OWNER"},
},
},
res: res{
@@ -366,10 +366,10 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) {
},
args: args{
member: &ChangeProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -410,10 +410,10 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) {
},
args: args{
member: &ChangeProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER"},
},
},
res: res{
@@ -467,10 +467,10 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) {
},
args: args{
member: &ChangeProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
},
},
res: res{
@@ -516,10 +516,10 @@ func TestCommandSide_ChangeProjectGrantMember(t *testing.T) {
},
args: args{
member: &ChangeProjectGrantMember{
ProjectID: "project1",
GrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
ProjectID: "project1",
ProjectGrantID: "projectgrant1",
UserID: "user1",
Roles: []string{"PROJECT_GRANT_OWNER", "PROJECT_GRANT_VIEWER"},
},
},
res: res{
@@ -554,10 +554,11 @@ func TestCommandSide_RemoveProjectGrantMember(t *testing.T) {
checkPermission domain.PermissionCheck
}
type args struct {
ctx context.Context
projectID string
grantID string
userID string
ctx context.Context
projectID string
grantID string
organizationID string
userID string
}
type res struct {
want *domain.ObjectDetails
@@ -693,6 +694,52 @@ func TestCommandSide_RemoveProjectGrantMember(t *testing.T) {
},
},
},
{
name: "member remove, organizationID, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
project.NewGrantAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"projectgrant1",
"org2",
[]string{"rol1", "role2"},
),
),
),
expectFilter(
eventFromEventPusher(
project.NewProjectGrantMemberAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"user1",
"projectgrant1",
[]string{"PROJECT_OWNER"}...,
),
),
),
expectPush(
project.NewProjectGrantMemberRemovedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"user1",
"projectgrant1",
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args: args{
ctx: context.Background(),
projectID: "project1",
userID: "user1",
organizationID: "org2",
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "member remove, no permission",
fields: fields{
@@ -737,7 +784,7 @@ func TestCommandSide_RemoveProjectGrantMember(t *testing.T) {
eventstore: tt.fields.eventstore(t),
checkPermission: tt.fields.checkPermission,
}
got, err := r.RemoveProjectGrantMember(tt.args.ctx, tt.args.projectID, tt.args.userID, tt.args.grantID)
got, err := r.RemoveProjectGrantMember(tt.args.ctx, tt.args.projectID, tt.args.userID, tt.args.grantID, tt.args.organizationID)
if tt.res.err == nil {
assert.NoError(t, err)
}

View File

@@ -1,6 +1,8 @@
package command
import (
"slices"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/org"
@@ -14,6 +16,8 @@ type ProjectGrantWriteModel struct {
GrantedOrgID string
RoleKeys []string
State domain.ProjectGrantState
FoundGrantID string
}
func NewProjectGrantWriteModel(grantID, grantedOrgID, projectID, resourceOwner string) *ProjectGrantWriteModel {
@@ -22,6 +26,7 @@ func NewProjectGrantWriteModel(grantID, grantedOrgID, projectID, resourceOwner s
AggregateID: projectID,
ResourceOwner: resourceOwner,
},
// Always either the grantID or the grantedOrgID is provided
GrantID: grantID,
GrantedOrgID: grantedOrgID,
}
@@ -31,28 +36,29 @@ func (wm *ProjectGrantWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *project.GrantAddedEvent:
if (wm.GrantID != "" && e.GrantID == wm.GrantID) ||
(wm.GrantedOrgID != "" && e.GrantedOrgID == wm.GrantedOrgID) {
if projectGrantExists(wm.GrantID, wm.GrantedOrgID, e.GrantID, e.GrantedOrgID) {
wm.FoundGrantID = e.GrantID
wm.WriteModel.AppendEvents(e)
}
case *project.GrantChangedEvent:
if wm.GrantID != "" && e.GrantID == wm.GrantID {
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
wm.WriteModel.AppendEvents(e)
}
case *project.GrantCascadeChangedEvent:
if wm.GrantID != "" && e.GrantID == wm.GrantID {
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
wm.WriteModel.AppendEvents(e)
}
case *project.GrantDeactivateEvent:
if wm.GrantID != "" && e.GrantID == wm.GrantID {
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
wm.WriteModel.AppendEvents(e)
}
case *project.GrantReactivatedEvent:
if wm.GrantID != "" && e.GrantID == wm.GrantID {
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
wm.WriteModel.AppendEvents(e)
}
case *project.GrantRemovedEvent:
if wm.GrantID != "" && e.GrantID == wm.GrantID {
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
wm.FoundGrantID = ""
wm.WriteModel.AppendEvents(e)
}
case *project.ProjectRemovedEvent:
@@ -61,6 +67,17 @@ func (wm *ProjectGrantWriteModel) AppendEvents(events ...eventstore.Event) {
}
}
func projectGrantExists(requiredGrantID, requiredGrantedOrgID, grantID, grantedOrgID string) bool {
// either grantID or grantedOrgID is provided and equal
return projectGrantEqual(requiredGrantID, grantID) ||
(requiredGrantedOrgID != "" && grantedOrgID == requiredGrantedOrgID)
}
func projectGrantEqual(requiredGrantID, grantID string) bool {
// grantID is provided and equal
return requiredGrantID != "" && grantID == requiredGrantID
}
func (wm *ProjectGrantWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
@@ -93,7 +110,8 @@ func (wm *ProjectGrantWriteModel) Reduce() error {
}
func (wm *ProjectGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(project.AggregateType).
AggregateIDs(wm.AggregateID).
@@ -106,11 +124,6 @@ func (wm *ProjectGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
project.GrantRemovedType,
project.ProjectRemovedType).
Builder()
if wm.ResourceOwner != "" {
query.ResourceOwner(wm.ResourceOwner)
}
return query
}
type ProjectGrantPreConditionReadModel struct {
@@ -159,14 +172,9 @@ func (wm *ProjectGrantPreConditionReadModel) Reduce() error {
if e.Aggregate().ResourceOwner != wm.ProjectResourceOwner {
continue
}
for i, key := range wm.ExistingRoleKeys {
if key == e.Key {
copy(wm.ExistingRoleKeys[i:], wm.ExistingRoleKeys[i+1:])
wm.ExistingRoleKeys[len(wm.ExistingRoleKeys)-1] = ""
wm.ExistingRoleKeys = wm.ExistingRoleKeys[:len(wm.ExistingRoleKeys)-1]
continue
}
}
wm.ExistingRoleKeys = slices.DeleteFunc(wm.ExistingRoleKeys, func(key string) bool {
return key == e.Key
})
case *org.OrgAddedEvent:
wm.GrantedOrgExists = true
case *org.OrgRemovedEvent:

View File

@@ -352,6 +352,61 @@ func TestCommandSide_AddProjectGrant(t *testing.T) {
},
},
},
{
name: "grant for project, only granted org, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").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", "org1").Aggregate,
"key1",
"key",
"",
),
),
),
expectPush(
project.NewGrantAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"projectgrant1",
"grantedorg1",
[]string{"key1"},
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "projectgrant1"),
},
args: args{
ctx: context.Background(),
projectGrant: &AddProjectGrant{
ObjectRoot: models.ObjectRoot{
AggregateID: "project1",
},
GrantedOrgID: "grantedorg1",
RoleKeys: []string{"key1"},
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -790,6 +845,75 @@ func TestCommandSide_ChangeProjectGrant(t *testing.T) {
},
},
},
{
name: "projectgrant only added roles,no resourceowner, grantedOrgID, ok",
fields: fields{
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(project.NewGrantAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"projectgrant1",
"grantedorg1",
[]string{"key1"},
)),
),
expectFilter(
eventFromEventPusher(
project.NewProjectAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").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", "org1").Aggregate,
"key1",
"key",
"",
),
),
eventFromEventPusher(
project.NewRoleAddedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"key2",
"key2",
"",
),
),
),
expectPush(
project.NewGrantChangedEvent(context.Background(),
&project.NewAggregate("project1", "org1").Aggregate,
"projectgrant1",
[]string{"key1", "key2"},
),
),
),
checkPermission: newMockPermissionCheckAllowed(),
},
args: args{
ctx: context.Background(),
projectGrant: &ChangeProjectGrant{
ObjectRoot: models.ObjectRoot{
AggregateID: "project1",
},
GrantedOrgID: "grantedorg1",
RoleKeys: []string{"key1", "key2"},
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "org1",
},
},
},
{
name: "projectgrant remove roles, usergrant not found, ok",
fields: fields{

View File

@@ -171,7 +171,8 @@ func projectExistsOnOrganization(requiredOrganization, projectResourceOwner stri
func projectGrantExistsOnOrganization(requiredGrantID, requiredOrganization, projectGrantID, grantedOrganization string) bool {
// Depending on the API, a request can either require a project grant (ID) and/or an organization (ID),
// where the project must be granted to.
return (requiredGrantID == "" || requiredGrantID == projectGrantID) &&
return (requiredGrantID != "" || requiredOrganization != "") &&
(requiredGrantID == "" || requiredGrantID == projectGrantID) &&
(requiredOrganization == "" || requiredOrganization == grantedOrganization)
}

View File

@@ -959,6 +959,25 @@ func (i *Instance) ActivateProjectGrant(ctx context.Context, t *testing.T, proje
return resp
}
func (i *Instance) CreateAuthorizationProject(t *testing.T, ctx context.Context, projectID, userID string) *authorization.CreateAuthorizationResponse {
resp, err := i.Client.AuthorizationV2Beta.CreateAuthorization(ctx, &authorization.CreateAuthorizationRequest{
UserId: userID,
ProjectId: projectID,
})
require.NoError(t, err)
return resp
}
func (i *Instance) CreateAuthorizationProjectGrant(t *testing.T, ctx context.Context, projectID, orgID, userID string) *authorization.CreateAuthorizationResponse {
resp, err := i.Client.AuthorizationV2Beta.CreateAuthorization(ctx, &authorization.CreateAuthorizationRequest{
UserId: userID,
ProjectId: projectID,
OrganizationId: gu.Ptr(orgID),
})
require.NoError(t, err)
return resp
}
func (i *Instance) CreateProjectUserGrant(t *testing.T, ctx context.Context, orgID, projectID, userID string) *mgmt.AddUserGrantResponse {
//nolint:staticcheck
resp, err := i.Client.Mgmt.AddUserGrant(SetOrgID(ctx, orgID), &mgmt.AddUserGrantRequest{
@@ -1042,12 +1061,12 @@ func (i *Instance) DeleteProjectMembership(t *testing.T, ctx context.Context, pr
require.NoError(t, err)
}
func (i *Instance) CreateProjectGrantMembership(t *testing.T, ctx context.Context, projectID, grantID, userID string) *internal_permission_v2beta.CreateAdministratorResponse {
func (i *Instance) CreateProjectGrantMembership(t *testing.T, ctx context.Context, projectID, orgID, userID string) *internal_permission_v2beta.CreateAdministratorResponse {
resp, err := i.Client.InternalPermissionv2Beta.CreateAdministrator(ctx, &internal_permission_v2beta.CreateAdministratorRequest{
Resource: &internal_permission_v2beta.ResourceType{
Resource: &internal_permission_v2beta.ResourceType_ProjectGrant_{ProjectGrant: &internal_permission_v2beta.ResourceType_ProjectGrant{
ProjectId: projectID,
ProjectGrantId: grantID,
OrganizationId: orgID,
}},
},
UserId: userID,
@@ -1057,12 +1076,12 @@ func (i *Instance) CreateProjectGrantMembership(t *testing.T, ctx context.Contex
return resp
}
func (i *Instance) DeleteProjectGrantMembership(t *testing.T, ctx context.Context, projectID, grantID, userID string) {
func (i *Instance) DeleteProjectGrantMembership(t *testing.T, ctx context.Context, projectID, orgID, userID string) {
_, err := i.Client.InternalPermissionv2Beta.DeleteAdministrator(ctx, &internal_permission_v2beta.DeleteAdministratorRequest{
Resource: &internal_permission_v2beta.ResourceType{
Resource: &internal_permission_v2beta.ResourceType_ProjectGrant_{ProjectGrant: &internal_permission_v2beta.ResourceType_ProjectGrant{
ProjectId: projectID,
ProjectGrantId: grantID,
OrganizationId: orgID,
}},
},
UserId: userID,

View File

@@ -313,10 +313,12 @@ message CreateAdministratorRequest {
message ResourceType {
message ProjectGrant {
reserved "project_grant_id";
reserved 2;
// ProjectID is required to grant administrator privileges for a specific project.
string project_id = 1;
// ProjectGrantID is required to grant administrator privileges for a specific project grant.
string project_grant_id = 2;
// OrganizationID is required to grant administrator privileges for a specific project grant.
string organization_id = 3;
}
// Resource is the type of the resource the administrator roles should be granted for.
@@ -381,4 +383,4 @@ message DeleteAdministratorResponse {
// Note that the deletion date is only guaranteed to be set if the deletion was successful during the request.
// In case the deletion occurred in a previous request, the deletion date might not be set.
google.protobuf.Timestamp deletion_date = 1;
}
}