diff --git a/internal/api/grpc/authorization/v2beta/integration_test/query_test.go b/internal/api/grpc/authorization/v2beta/integration_test/query_test.go index d623da829..cc342690c 100644 --- a/internal/api/grpc/authorization/v2beta/integration_test/query_test.go +++ b/internal/api/grpc/authorization/v2beta/integration_test/query_test.go @@ -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]) } } diff --git a/internal/api/grpc/internal_permission/v2beta/administrator.go b/internal/api/grpc/internal_permission/v2beta/administrator.go index 86ee7d945..21f3ef1c6 100644 --- a/internal/api/grpc/internal_permission/v2beta/administrator.go +++ b/internal/api/grpc/internal_permission/v2beta/administrator.go @@ -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 } diff --git a/internal/api/grpc/internal_permission/v2beta/integration_test/administrator_test.go b/internal/api/grpc/internal_permission/v2beta/integration_test/administrator_test.go index 3432f5fd3..f5537c12b 100644 --- a/internal/api/grpc/internal_permission/v2beta/integration_test/administrator_test.go +++ b/internal/api/grpc/internal_permission/v2beta/integration_test/administrator_test.go @@ -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(), }, }, }, diff --git a/internal/api/grpc/internal_permission/v2beta/integration_test/query_test.go b/internal/api/grpc/internal_permission/v2beta/integration_test/query_test.go index 687d4de2f..8553fcc92 100644 --- a/internal/api/grpc/internal_permission/v2beta/integration_test/query_test.go +++ b/internal/api/grpc/internal_permission/v2beta/integration_test/query_test.go @@ -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]) } } diff --git a/internal/api/grpc/management/project_grant.go b/internal/api/grpc/management/project_grant.go index 26f51f685..615991fc8 100644 --- a/internal/api/grpc/management/project_grant.go +++ b/internal/api/grpc/management/project_grant.go @@ -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 } diff --git a/internal/api/grpc/management/project_grant_converter.go b/internal/api/grpc/management/project_grant_converter.go index 0523eed13..58e359c36 100644 --- a/internal/api/grpc/management/project_grant_converter.go +++ b/internal/api/grpc/management/project_grant_converter.go @@ -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, } } diff --git a/internal/api/grpc/oidc/v2/integration_test/oidc_test.go b/internal/api/grpc/oidc/v2/integration_test/oidc_test.go index f4f99cd78..5cba4fc37 100644 --- a/internal/api/grpc/oidc/v2/integration_test/oidc_test.go +++ b/internal/api/grpc/oidc/v2/integration_test/oidc_test.go @@ -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) diff --git a/internal/api/grpc/oidc/v2beta/integration_test/oidc_test.go b/internal/api/grpc/oidc/v2beta/integration_test/oidc_test.go index 1ad63cea1..0f248513d 100644 --- a/internal/api/grpc/oidc/v2beta/integration_test/oidc_test.go +++ b/internal/api/grpc/oidc/v2beta/integration_test/oidc_test.go @@ -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) diff --git a/internal/api/grpc/project/v2beta/project_grant.go b/internal/api/grpc/project/v2beta/project_grant.go index c2ccef175..802c68b99 100644 --- a/internal/api/grpc/project/v2beta/project_grant.go +++ b/internal/api/grpc/project/v2beta/project_grant.go @@ -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 } diff --git a/internal/api/grpc/saml/v2/integration_test/saml_test.go b/internal/api/grpc/saml/v2/integration_test/saml_test.go index 2c0ccd7e1..d5c3e3d38 100644 --- a/internal/api/grpc/saml/v2/integration_test/saml_test.go +++ b/internal/api/grpc/saml/v2/integration_test/saml_test.go @@ -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) diff --git a/internal/command/permission_checks.go b/internal/command/permission_checks.go index 021107e6a..65ab3fa01 100644 --- a/internal/command/permission_checks.go +++ b/internal/command/permission_checks.go @@ -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 { diff --git a/internal/command/project_grant.go b/internal/command/project_grant.go index 2a0e83a6e..2a1611e10 100644 --- a/internal/command/project_grant.go +++ b/internal/command/project_grant.go @@ -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) { diff --git a/internal/command/project_grant_member.go b/internal/command/project_grant_member.go index 611b897c6..b7f2cf02f 100644 --- a/internal/command/project_grant_member.go +++ b/internal/command/project_grant_member.go @@ -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) diff --git a/internal/command/project_grant_member_test.go b/internal/command/project_grant_member_test.go index 3cd8ecdd8..f6a48a5dc 100644 --- a/internal/command/project_grant_member_test.go +++ b/internal/command/project_grant_member_test.go @@ -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) } diff --git a/internal/command/project_grant_model.go b/internal/command/project_grant_model.go index 15950d4f3..9ef466c0d 100644 --- a/internal/command/project_grant_model.go +++ b/internal/command/project_grant_model.go @@ -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: diff --git a/internal/command/project_grant_test.go b/internal/command/project_grant_test.go index 7a3bb98e7..518249f0a 100644 --- a/internal/command/project_grant_test.go +++ b/internal/command/project_grant_test.go @@ -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{ diff --git a/internal/command/user_grant_model.go b/internal/command/user_grant_model.go index b9d9d27a2..cc1062bf7 100644 --- a/internal/command/user_grant_model.go +++ b/internal/command/user_grant_model.go @@ -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) } diff --git a/internal/integration/client.go b/internal/integration/client.go index 78f07425c..fae837c8e 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -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, diff --git a/proto/zitadel/internal_permission/v2beta/internal_permission_service.proto b/proto/zitadel/internal_permission/v2beta/internal_permission_service.proto index 3a27b89a4..977d95fed 100644 --- a/proto/zitadel/internal_permission/v2beta/internal_permission_service.proto +++ b/proto/zitadel/internal_permission/v2beta/internal_permission_service.proto @@ -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; -} \ No newline at end of file +}