mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-07 06:42:15 +00:00
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:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user