feat(group): add group permissions (#10853)

# Which Problems Are Solved
Ensuring the user group resource is managed with appropriate
permissions.

# How the Problems Are Solved
By configuring and checking for the relevant permissions needed to
create, read, update, and delete the user groups resource.

# Additional Changes
N/A

# Additional Context
- Related to #9702 
- Follow-up for PRs #10455,  #10758
This commit is contained in:
Gayathri Vijayan
2025-10-21 11:18:21 +02:00
committed by GitHub
parent ff869482b1
commit ebc6c503a3
13 changed files with 1065 additions and 145 deletions
@@ -26,14 +26,24 @@ func TestServer_CreateGroup(t *testing.T) {
resp := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), alreadyExistingGroupName)
tests := []struct {
name string
ctx context.Context
req *group_v2.CreateGroupRequest
wantResp bool
wantGroupID string
wantErrorCode codes.Code
wantErrMsg string
name string
ctx context.Context
req *group_v2.CreateGroupRequest
wantResp bool
wantGroupID string
wantErrCode codes.Code
wantErrMsg string
}{
{
name: "unauthenticated, error",
ctx: context.Background(),
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrCode: codes.Unauthenticated,
wantErrMsg: "auth header missing",
},
{
name: "invalid name, error",
ctx: iamOwnerCtx,
@@ -41,8 +51,8 @@ func TestServer_CreateGroup(t *testing.T) {
Name: " ",
OrganizationId: "org1",
},
wantErrorCode: codes.InvalidArgument,
wantErrMsg: "Errors.Group.InvalidName (GROUP-m177lN)",
wantErrCode: codes.InvalidArgument,
wantErrMsg: "Errors.Group.InvalidName (GROUP-m177lN)",
},
{
name: "missing organization id, error",
@@ -50,8 +60,18 @@ func TestServer_CreateGroup(t *testing.T) {
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
},
wantErrorCode: codes.InvalidArgument,
wantErrMsg: "invalid CreateGroupRequest.OrganizationId: value length must be between 1 and 200 runes, inclusive",
wantErrCode: codes.InvalidArgument,
wantErrMsg: "invalid CreateGroupRequest.OrganizationId: value length must be between 1 and 200 runes, inclusive",
},
{
name: "missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization not found, error",
@@ -60,32 +80,51 @@ func TestServer_CreateGroup(t *testing.T) {
Name: integration.GroupName(),
OrganizationId: "org1",
},
wantErrorCode: codes.FailedPrecondition,
wantErrMsg: "Organisation not found (CMDGRP-j1mH8l)",
wantErrCode: codes.FailedPrecondition,
wantErrMsg: "Organisation not found (CMDGRP-j1mH8l)",
},
{
name: "already existing group (unique name constraint), error",
name: "instance owner, already existing group (unique name constraint), error",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: alreadyExistingGroupName,
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrorCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (V3-DKcYh)",
wantErrCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (V3-DKcYh)",
},
{
name: "already existing group ID, error",
name: "instance owner, already existing group ID, error",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Id: gu.Ptr(resp.Id),
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrorCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (CMDGRP-shRut3)",
wantErrCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (CMDGRP-shRut3)",
},
{
name: "create group with ID, ok",
name: "organization owner, missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization owner, with permission, ok",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: instance.DefaultOrg.GetId(),
},
wantResp: true,
},
{
name: "instance owner, create group with ID, ok",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Id: gu.Ptr("1234"),
@@ -96,7 +135,7 @@ func TestServer_CreateGroup(t *testing.T) {
wantGroupID: "1234",
},
{
name: "create group without user provided ID, ok",
name: "instance owner, create group without user provided ID, ok",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
@@ -108,11 +147,11 @@ func TestServer_CreateGroup(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := instance.Client.GroupV2.CreateGroup(tt.ctx, tt.req)
if tt.wantErrorCode != codes.OK {
if tt.wantErrCode != codes.OK {
require.Error(t, err)
require.Empty(t, got.GetId())
require.Empty(t, got.GetCreationDate())
assert.Equal(t, tt.wantErrorCode, status.Code(err))
assert.Equal(t, tt.wantErrCode, status.Code(err))
assert.Equal(t, tt.wantErrMsg, status.Convert(err).Message())
return
}
@@ -129,8 +168,12 @@ func TestServer_CreateGroup(t *testing.T) {
func TestServer_UpdateGroup(t *testing.T) {
iamOwnerCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
// create a group in the default org
groupDefOrg := instance.CreateGroup(iamOwnerCtx, t, instance.DefaultOrg.GetId(), integration.GroupName())
// create a new org
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
// create a group in the new org
groupName := integration.GroupName()
existingGroup := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
@@ -142,6 +185,15 @@ func TestServer_UpdateGroup(t *testing.T) {
wantErrCode codes.Code
wantErrMsg string
}{
{
name: "unauthenticated, error",
ctx: context.Background(),
req: &group_v2.UpdateGroupRequest{
Name: gu.Ptr(integration.GroupName()),
},
wantErrCode: codes.Unauthenticated,
wantErrMsg: "auth header missing",
},
{
name: "invalid name, error",
ctx: iamOwnerCtx,
@@ -153,17 +205,46 @@ func TestServer_UpdateGroup(t *testing.T) {
wantErrMsg: "Errors.Group.InvalidName (GROUP-dUNd3r)",
},
{
name: "group not found, error",
name: "missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr("updated group name"),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization owner, missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr("updated group name"),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization owner, with permission, ok",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.UpdateGroupRequest{
Id: groupDefOrg.GetId(),
Name: gu.Ptr("updated group name 1"),
},
wantChangeDate: true,
},
{
name: "instance owner, group not found, error",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: "12345",
Name: gu.Ptr("updated group name"),
Name: gu.Ptr("updated group name 2"),
},
wantErrCode: codes.NotFound,
wantErrMsg: "Errors.Group.NotFound (CMDGRP-b33zly)",
},
{
name: "no change, ok",
name: "instance owner, no change, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
@@ -172,7 +253,7 @@ func TestServer_UpdateGroup(t *testing.T) {
wantChangeDate: true,
},
{
name: "change name, ok",
name: "instance owner, change name, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
@@ -181,7 +262,7 @@ func TestServer_UpdateGroup(t *testing.T) {
wantChangeDate: true,
},
{
name: "change description, ok",
name: "instance owner, change description, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
@@ -190,7 +271,7 @@ func TestServer_UpdateGroup(t *testing.T) {
wantChangeDate: true,
},
{
name: "full change, ok",
name: "instance owner, full change, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
@@ -218,11 +299,18 @@ func TestServer_UpdateGroup(t *testing.T) {
func TestServer_DeleteGroup(t *testing.T) {
iamOwnerCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
// create a group in the default org
groupDefOrg := instance.CreateGroup(iamOwnerCtx, t, instance.DefaultOrg.GetId(), integration.GroupName())
// create a new org
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
// create a group in the new org
groupName := integration.GroupName()
existingGroup := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
// create a group in the new org to be deleted before the test
deleteGroupName := integration.GroupName()
deleteGroup := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), deleteGroupName)
deleteResp := instance.DeleteGroup(iamOwnerCtx, t, deleteGroup.GetId())
@@ -235,6 +323,15 @@ func TestServer_DeleteGroup(t *testing.T) {
wantErrMsg string
deletionTime *timestamp.Timestamp
}{
{
name: "unauthenticated, error",
ctx: context.Background(),
req: &group_v2.DeleteGroupRequest{
Id: "12345",
},
wantErrCode: codes.Unauthenticated,
wantErrMsg: "auth header missing",
},
{
name: "missing id, error",
ctx: iamOwnerCtx,
@@ -244,6 +341,31 @@ func TestServer_DeleteGroup(t *testing.T) {
wantErrCode: codes.InvalidArgument,
wantErrMsg: "invalid DeleteGroupRequest.Id: value length must be between 1 and 200 runes, inclusive",
},
{
name: "missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
req: &group_v2.DeleteGroupRequest{
Id: existingGroup.GetId(),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization owner, missing permission, error",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.DeleteGroupRequest{
Id: existingGroup.GetId(),
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "organization owner, with permission, ok",
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
req: &group_v2.DeleteGroupRequest{
Id: groupDefOrg.GetId(),
},
},
{
name: "group not found, ok",
ctx: iamOwnerCtx,
@@ -50,7 +50,7 @@ func TestServer_GetGroup(t *testing.T) {
wantErrMsg: "invalid GetGroupRequest.Id: value length must be between 1 and 200 runes, inclusive",
},
{
name: "get group, not found",
name: "get group, instance owner, not found",
args: args{
ctx: iamOwnerCtx,
req: &group_v2.GetGroupRequest{
@@ -61,7 +61,62 @@ func TestServer_GetGroup(t *testing.T) {
wantErrMsg: "Errors.Group.NotFound (QUERY-SG4WbR)",
},
{
name: "get group, found",
name: "get group, missing permission, error",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
dep: func(req *group_v2.GetGroupRequest, resp *group_v2.GetGroupResponse) {
groupName := integration.GroupName()
group := instance.CreateGroup(iamOwnerCtx, t, instance.DefaultOrg.GetId(), groupName)
req.Id = group.GetId()
},
req: &group_v2.GetGroupRequest{},
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "get group, organization owner, missing permission, error",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.GetGroupRequest, resp *group_v2.GetGroupResponse) {
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
req.Id = group.GetId()
},
req: &group_v2.GetGroupRequest{},
},
wantErrCode: codes.NotFound,
wantErrMsg: "membership not found (AUTHZ-cdgFk)",
},
{
name: "get group, organization owner, with permission, found",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.GetGroupRequest, resp *group_v2.GetGroupResponse) {
groupName := integration.GroupName()
group := instance.CreateGroup(iamOwnerCtx, t, instance.DefaultOrg.GetId(), groupName)
req.Id = group.GetId()
resp.Group = &group_v2.Group{
Id: group.GetId(),
Name: groupName,
Description: "",
OrganizationId: instance.DefaultOrg.GetId(),
ChangeDate: group.GetCreationDate(),
CreationDate: group.GetCreationDate(),
}
},
req: &group_v2.GetGroupRequest{},
},
want: &group_v2.GetGroupResponse{
Group: &group_v2.Group{},
},
},
{
name: "get group, instance owner, found",
args: args{
ctx: iamOwnerCtx,
dep: func(req *group_v2.GetGroupRequest, resp *group_v2.GetGroupResponse) {
@@ -132,7 +187,97 @@ func TestServer_ListGroups(t *testing.T) {
wantErrMsg: "auth header missing",
},
{
name: "group ID not found",
name: "no permission, empty list, TotalResult count returned",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{},
},
},
{
name: "org owner, missing permission, empty list, TotalResult count returned",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{},
},
},
{
name: "org owner, with permission, ok",
args: args{
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
groupName := integration.GroupName()
group1 := instance.CreateGroup(iamOwnerCtx, t, instance.DefaultOrg.GetId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
resp.Groups[0] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName,
Description: "",
OrganizationId: instance.DefaultOrg.GetId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{},
},
},
},
{
name: "instance owner, group ID not found",
args: args{
ctx: iamOwnerCtx,
req: &group_v2.ListGroupsRequest{
@@ -367,3 +512,354 @@ func TestServer_ListGroups(t *testing.T) {
})
}
}
func TestServer_ListGroups_WithPermissionV2(t *testing.T) {
ensureFeaturePermissionV2Enabled(t, instancePermissionV2)
iamOwnerCtx := instancePermissionV2.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
req *group_v2.ListGroupsRequest
dep func(*group_v2.ListGroupsRequest, *group_v2.ListGroupsResponse)
}
tests := []struct {
name string
args args
want *group_v2.ListGroupsResponse
wantErrCode codes.Code
wantErrMsg string
}{
{
name: "list groups, unauthenticated",
args: args{
ctx: CTX,
req: &group_v2.ListGroupsRequest{},
},
wantErrCode: codes.Unauthenticated,
wantErrMsg: "auth header missing",
},
{
name: "no permission, empty list, TotalResult count set to 0",
args: args{
ctx: instancePermissionV2.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 0,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{},
},
},
{
name: "org owner, missing permission, empty list, TotalResult count set to 0",
args: args{
ctx: instancePermissionV2.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 0,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{},
},
},
{
name: "org owner, with permission, ok",
args: args{
ctx: instancePermissionV2.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
groupName := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, instancePermissionV2.DefaultOrg.GetId(), groupName)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
resp.Groups[0] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName,
Description: "",
OrganizationId: instancePermissionV2.DefaultOrg.GetId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{},
},
},
},
{
name: "instance owner, group ID not found",
args: args{
ctx: iamOwnerCtx,
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{
{
Filter: &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{"random-group"},
},
},
},
},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 0,
AppliedLimit: 100,
},
},
},
{
name: "list single group by ID, ok",
args: args{
ctx: iamOwnerCtx,
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
resp.Groups[0] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName,
Description: "",
OrganizationId: orgResp.GetOrganizationId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{},
},
},
},
{
name: "list multiple groups by IDs, ok",
args: args{
ctx: iamOwnerCtx,
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName1 := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName1)
resp.Groups[1] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName1,
Description: "",
OrganizationId: orgResp.GetOrganizationId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
groupName2 := integration.GroupName()
group2 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName2)
resp.Groups[0] = &group_v2.Group{
Id: group2.GetId(),
Name: groupName2,
Description: "",
OrganizationId: orgResp.GetOrganizationId(),
CreationDate: group2.GetCreationDate(),
ChangeDate: group2.GetCreationDate(),
}
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_GroupIds{
GroupIds: &filter.InIDsFilter{
Ids: []string{group1.GetId(), group2.GetId()},
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 2,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{}, {},
},
},
},
{
name: "list group by name, ok",
args: args{
ctx: iamOwnerCtx,
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
orgResp := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), groupName)
resp.Groups[0] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName,
Description: "",
OrganizationId: orgResp.GetOrganizationId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_NameFilter{
NameFilter: &group_v2.GroupNameFilter{
Name: groupName,
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{},
},
},
},
{
name: "list by organization ID, ok",
args: args{
ctx: iamOwnerCtx,
dep: func(req *group_v2.ListGroupsRequest, resp *group_v2.ListGroupsResponse) {
org1 := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
groupName2 := integration.GroupName()
group2 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, org1.GetOrganizationId(), groupName2)
resp.Groups[2] = &group_v2.Group{
Id: group2.GetId(),
Name: groupName2,
Description: "",
OrganizationId: org1.GetOrganizationId(),
CreationDate: group2.GetCreationDate(),
ChangeDate: group2.GetCreationDate(),
}
groupName1 := integration.GroupName()
group1 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, org1.GetOrganizationId(), groupName1)
resp.Groups[1] = &group_v2.Group{
Id: group1.GetId(),
Name: groupName1,
Description: "",
OrganizationId: org1.GetOrganizationId(),
CreationDate: group1.GetCreationDate(),
ChangeDate: group1.GetCreationDate(),
}
groupName0 := integration.GroupName()
group0 := instancePermissionV2.CreateGroup(iamOwnerCtx, t, org1.GetOrganizationId(), groupName0)
resp.Groups[0] = &group_v2.Group{
Id: group0.GetId(),
Name: groupName0,
Description: "",
OrganizationId: org1.GetOrganizationId(),
CreationDate: group0.GetCreationDate(),
ChangeDate: group0.GetCreationDate(),
}
org2 := instancePermissionV2.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
org2GroupName0 := integration.GroupName()
_ = instancePermissionV2.CreateGroup(iamOwnerCtx, t, org2.GetOrganizationId(), org2GroupName0)
req.Filters[0].Filter = &group_v2.GroupsSearchFilter_OrganizationId{
OrganizationId: &filter.IDFilter{
Id: org1.GetOrganizationId(),
},
}
},
req: &group_v2.ListGroupsRequest{
Filters: []*group_v2.GroupsSearchFilter{{}},
},
},
want: &group_v2.ListGroupsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 3,
AppliedLimit: 100,
},
Groups: []*group_v2.Group{
{}, {}, {},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
tt.args.dep(tt.args.req, tt.want)
}
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCtx, time.Minute)
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, err := instancePermissionV2.Client.GroupV2.ListGroups(tt.args.ctx, tt.args.req)
if tt.wantErrCode != codes.OK {
require.Error(ttt, err)
assert.Equal(ttt, tt.wantErrCode, status.Code(err))
assert.Equal(ttt, tt.wantErrMsg, status.Convert(err).Message())
return
}
require.NoError(ttt, err)
if assert.Len(ttt, got.Groups, len(tt.want.Groups)) {
for i := range got.Groups {
assert.EqualExportedValues(ttt, tt.want.Groups[i], got.Groups[i], "want: %v, got: %v", tt.want.Groups[i], got.Groups[i])
}
}
assert.Equal(ttt, tt.want.Pagination.AppliedLimit, got.Pagination.AppliedLimit)
assert.Equal(ttt, tt.want.Pagination.TotalResult, got.Pagination.TotalResult)
}, retryDuration, tick, "timeout waiting for expected result")
})
}
}
@@ -8,12 +8,18 @@ import (
"testing"
"time"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/integration"
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
)
var (
CTX context.Context
instance *integration.Instance
CTX context.Context
instance *integration.Instance
instancePermissionV2 *integration.Instance
)
func TestMain(m *testing.M) {
@@ -22,6 +28,31 @@ func TestMain(m *testing.M) {
defer cancel()
CTX = ctx
instance = integration.NewInstance(ctx)
instancePermissionV2 = integration.NewInstance(ctx)
return m.Run()
}())
}
func ensureFeaturePermissionV2Enabled(t *testing.T, testInstance *integration.Instance) {
ctx := testInstance.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner)
f, err := testInstance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(t, err)
if f.PermissionCheckV2.GetEnabled() {
return
}
_, err = testInstance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
PermissionCheckV2: gu.Ptr(true),
})
require.NoError(t, err)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
require.EventuallyWithT(t, func(tt *assert.CollectT) {
f, err := testInstance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{Inheritance: true})
require.NoError(tt, err)
assert.True(tt, f.PermissionCheckV2.GetEnabled())
}, retryDuration, tick, "timed out waiting for ensuring testInstance feature")
}
+2 -2
View File
@@ -15,7 +15,7 @@ import (
// GetGroup returns a group that matches the group ID in the request
func (s *Server) GetGroup(ctx context.Context, req *connect.Request[group_v2.GetGroupRequest]) (*connect.Response[group_v2.GetGroupResponse], error) {
group, err := s.query.GetGroupByID(ctx, req.Msg.GetId())
group, err := s.query.GetGroupByID(ctx, req.Msg.GetId(), s.checkPermission)
if err != nil {
return nil, err
}
@@ -30,7 +30,7 @@ func (s *Server) ListGroups(ctx context.Context, req *connect.Request[group_v2.L
if err != nil {
return nil, err
}
resp, err := s.query.SearchGroups(ctx, queries)
resp, err := s.query.SearchGroups(ctx, queries, s.checkPermission)
if err != nil {
return nil, err
}