Files
zitadel/internal/api/grpc/group/v2/integration_test/group_test.go
T
Gayathri VijayanandGitHub ebc6c503a3 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
2025-10-21 11:18:21 +02:00

410 lines
12 KiB
Go

//go:build integration
package group_test
import (
"context"
"testing"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/zitadel/zitadel/internal/integration"
group_v2 "github.com/zitadel/zitadel/pkg/grpc/group/v2"
)
func TestServer_CreateGroup(t *testing.T) {
iamOwnerCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
orgResp := instance.CreateOrganization(iamOwnerCtx, integration.OrganizationName(), integration.Email())
alreadyExistingGroupName := integration.GroupName()
resp := instance.CreateGroup(iamOwnerCtx, t, orgResp.GetOrganizationId(), alreadyExistingGroupName)
tests := []struct {
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,
req: &group_v2.CreateGroupRequest{
Name: " ",
OrganizationId: "org1",
},
wantErrCode: codes.InvalidArgument,
wantErrMsg: "Errors.Group.InvalidName (GROUP-m177lN)",
},
{
name: "missing organization id, error",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
},
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",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: "org1",
},
wantErrCode: codes.FailedPrecondition,
wantErrMsg: "Organisation not found (CMDGRP-j1mH8l)",
},
{
name: "instance owner, already existing group (unique name constraint), error",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: alreadyExistingGroupName,
OrganizationId: orgResp.GetOrganizationId(),
},
wantErrCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (V3-DKcYh)",
},
{
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(),
},
wantErrCode: codes.AlreadyExists,
wantErrMsg: "Errors.Group.AlreadyExists (CMDGRP-shRut3)",
},
{
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"),
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantResp: true,
wantGroupID: "1234",
},
{
name: "instance owner, create group without user provided ID, ok",
ctx: iamOwnerCtx,
req: &group_v2.CreateGroupRequest{
Name: integration.GroupName(),
OrganizationId: orgResp.GetOrganizationId(),
},
wantResp: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := instance.Client.GroupV2.CreateGroup(tt.ctx, tt.req)
if tt.wantErrCode != codes.OK {
require.Error(t, err)
require.Empty(t, got.GetId())
require.Empty(t, got.GetCreationDate())
assert.Equal(t, tt.wantErrCode, status.Code(err))
assert.Equal(t, tt.wantErrMsg, status.Convert(err).Message())
return
}
require.NoError(t, err)
if tt.wantGroupID != "" {
assert.Equal(t, tt.wantGroupID, got.Id, "want: %v, got: %v", tt.wantGroupID, got)
}
require.NotEmpty(t, got.GetId())
require.NotEmpty(t, got.GetCreationDate())
})
}
}
func TestServer_UpdateGroup(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)
tests := []struct {
name string
ctx context.Context
req *group_v2.UpdateGroupRequest
wantChangeDate bool
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,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr(" "),
},
wantErrCode: codes.InvalidArgument,
wantErrMsg: "Errors.Group.InvalidName (GROUP-dUNd3r)",
},
{
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 2"),
},
wantErrCode: codes.NotFound,
wantErrMsg: "Errors.Group.NotFound (CMDGRP-b33zly)",
},
{
name: "instance owner, no change, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr(groupName),
},
wantChangeDate: true,
},
{
name: "instance owner, change name, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr("groupXX"),
},
wantChangeDate: true,
},
{
name: "instance owner, change description, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Description: gu.Ptr("description of groupXX"),
},
wantChangeDate: true,
},
{
name: "instance owner, full change, ok",
ctx: iamOwnerCtx,
req: &group_v2.UpdateGroupRequest{
Id: existingGroup.GetId(),
Name: gu.Ptr("groupXXX"),
Description: gu.Ptr("description of groupXXX"),
},
wantChangeDate: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := instance.Client.GroupV2.UpdateGroup(tt.ctx, tt.req)
if tt.wantErrCode != codes.OK {
require.Error(t, err)
require.Empty(t, got.GetChangeDate())
assert.Equal(t, tt.wantErrCode, status.Code(err))
assert.Equal(t, tt.wantErrMsg, status.Convert(err).Message())
return
}
require.NoError(t, err)
require.NotEmpty(t, got.GetChangeDate())
})
}
}
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())
tests := []struct {
name string
ctx context.Context
req *group_v2.DeleteGroupRequest
wantErrCode codes.Code
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,
req: &group_v2.DeleteGroupRequest{
Id: "",
},
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,
req: &group_v2.DeleteGroupRequest{
Id: "12345",
},
},
{
name: "delete, ok",
ctx: iamOwnerCtx,
req: &group_v2.DeleteGroupRequest{
Id: existingGroup.GetId(),
},
},
{
name: "delete already deleted group, ok",
ctx: iamOwnerCtx,
req: &group_v2.DeleteGroupRequest{
Id: deleteGroup.GetId(),
},
deletionTime: deleteResp.GetDeletionDate(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := instance.Client.GroupV2.DeleteGroup(tt.ctx, tt.req)
if tt.wantErrCode != codes.OK {
require.Error(t, err)
require.Empty(t, got.GetDeletionDate())
assert.Equal(t, tt.wantErrCode, status.Code(err))
assert.Equal(t, tt.wantErrMsg, status.Convert(err).Message())
return
}
require.NoError(t, err)
require.NotEmpty(t, got.GetDeletionDate())
if tt.deletionTime != nil {
assert.Equal(t, tt.deletionTime, got.GetDeletionDate())
}
})
}
}