Files
zitadel/internal/query/group_test.go
Gayathri Vijayan 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

428 lines
9.1 KiB
Go

package query
import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"regexp"
"testing"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
prepareGroupsStmt = `SELECT projections.groups1.id,` +
` projections.groups1.name,` +
` projections.groups1.description,` +
` projections.groups1.creation_date,` +
` projections.groups1.change_date,` +
` projections.groups1.resource_owner,` +
` projections.groups1.instance_id,` +
` projections.groups1.sequence,` +
` projections.groups1.state,` +
` COUNT(*) OVER ()` +
` FROM projections.groups1`
prepareGroupsColumns = []string{
"id",
"name",
"description",
"creation_date",
"change_date",
"resource_owner",
"instance_id",
"sequence",
"state",
"count",
}
prepareGroupStmt = `SELECT projections.groups1.id,` +
` projections.groups1.name,` +
` projections.groups1.description,` +
` projections.groups1.creation_date,` +
` projections.groups1.change_date,` +
` projections.groups1.resource_owner,` +
` projections.groups1.instance_id,` +
` projections.groups1.sequence,` +
` projections.groups1.state` +
` FROM projections.groups1`
prepareGroupColumns = []string{
"id",
"name",
"description",
"creation_date",
"change_date",
"resource_owner",
"instance_id",
"sequence",
"state",
}
)
func Test_GroupPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := []struct {
name string
prepare interface{}
want want
object interface{}
}{
{
name: "prepareGroupsQuery, no result",
prepare: prepareGroupsQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(prepareGroupsStmt),
nil,
nil,
),
},
object: &Groups{Groups: []*Group{}},
},
{
name: "prepareGroupsQuery, one result",
prepare: prepareGroupsQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(prepareGroupsStmt),
prepareGroupsColumns,
[][]driver.Value{
{
"9090",
"group1",
"my new group",
testNow,
testNow,
"org1",
"instance1",
1,
domain.GroupStateActive,
},
},
),
},
object: &Groups{
SearchResponse: SearchResponse{
Count: 1,
},
Groups: []*Group{
{
ID: "9090",
Name: "group1",
Description: "my new group",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "org1",
InstanceID: "instance1",
Sequence: 1,
State: domain.GroupStateActive,
},
},
},
},
{
name: "prepareGroupsQuery, multiple results",
prepare: prepareGroupsQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(prepareGroupsStmt),
prepareGroupsColumns,
[][]driver.Value{
{
"9091",
"group1",
"my first group",
testNow,
testNow,
"org1",
"instance1",
1,
domain.GroupStateActive,
},
{
"9092",
"group2",
"my second group",
testNow,
testNow,
"org1",
"instance1",
1,
domain.GroupStateActive,
},
},
),
},
object: &Groups{
SearchResponse: SearchResponse{
Count: 2,
},
Groups: []*Group{
{
ID: "9091",
Name: "group1",
Description: "my first group",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "org1",
InstanceID: "instance1",
Sequence: 1,
State: domain.GroupStateActive,
},
{
ID: "9092",
Name: "group2",
Description: "my second group",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "org1",
InstanceID: "instance1",
Sequence: 1,
State: domain.GroupStateActive,
},
},
},
},
{
name: "prepareGroupsQuery, sql err",
prepare: prepareGroupsQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(prepareGroupsStmt),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: (*Groups)(nil),
},
{
name: "prepareGroupsQuery, no result",
prepare: prepareGroupsQuery,
want: want{
sqlExpectations: mockQueriesScanErr(
regexp.QuoteMeta(prepareGroupsStmt),
nil,
nil,
),
err: func(err error) (error, bool) {
if !zerrors.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: &Groups{
SearchResponse: SearchResponse{
Count: 0,
},
Groups: []*Group{},
},
},
{
name: "prepareGroupQuery, no result",
prepare: prepareGroupQuery,
want: want{
sqlExpectations: mockQueriesScanErr(
prepareGroupStmt,
nil,
nil,
),
err: func(err error) (error, bool) {
if !zerrors.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: (*Group)(nil),
},
{
name: "prepareGroupQuery, sql err",
prepare: prepareGroupQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(prepareGroupStmt),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: (*Group)(nil),
},
{
name: "prepareGroupQuery, found",
prepare: prepareGroupQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(prepareGroupStmt),
prepareGroupColumns,
[]driver.Value{
"9090",
"group1",
"my new group",
testNow,
testNow,
"org1",
"instance1",
1,
domain.GroupStateActive,
},
),
},
object: &Group{
ID: "9090",
Name: "group1",
Description: "my new group",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "org1",
InstanceID: "instance1",
Sequence: 1,
State: domain.GroupStateActive,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
})
}
}
func Test_GroupsCheckPermission(t *testing.T) {
tests := []struct {
name string
want []*Group
groups *Groups
permissions []string
}{
{
name: "no permissions",
want: []*Group{},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{},
},
{
name: "permissions for all groups",
want: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group1", "group2", "group3"},
},
{
name: "permissions for group1",
want: []*Group{
{ID: "group1"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group1"},
},
{
name: "permissions for group2",
want: []*Group{
{ID: "group2"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group2"},
},
{
name: "permissions for group3",
want: []*Group{
{ID: "group3"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group3"},
},
{
name: "permissions for group1 and group2",
want: []*Group{
{ID: "group1"}, {ID: "group2"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group1", "group2"},
},
{
name: "permissions for group1 and group3",
want: []*Group{
{ID: "group1"}, {ID: "group3"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group1", "group3"},
},
{
name: "permissions for group2 and group3",
want: []*Group{
{ID: "group2"}, {ID: "group3"},
},
groups: &Groups{
Groups: []*Group{
{ID: "group1"}, {ID: "group2"}, {ID: "group3"},
},
},
permissions: []string{"group2", "group3"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
for _, perm := range tt.permissions {
if resourceID == perm {
return nil
}
}
return errors.New("failed")
}
groupsCheckPermission(context.Background(), tt.groups, checkPermission)
require.Equal(t, tt.want, tt.groups.Groups)
})
}
}