diff --git a/internal/proxy/privilege_interceptor.go b/internal/proxy/privilege_interceptor.go index 645d41dd90..a25aa392f0 100644 --- a/internal/proxy/privilege_interceptor.go +++ b/internal/proxy/privilege_interceptor.go @@ -41,7 +41,7 @@ p = sub, obj, act e = some(where (p.eft == allow)) [matchers] -m = r.sub == p.sub && globMatch(r.obj, p.obj) && globMatch(r.act, p.act) || r.sub == "admin" || (r.sub == p.sub && dbMatch(r.obj, p.obj) && privilegeGroupContains(r.act, p.act)) +m = r.sub == p.sub && globMatch(r.obj, p.obj) && globMatch(r.act, p.act) || r.sub == "admin" || (r.sub == p.sub && dbMatch(r.obj, p.obj) && privilegeGroupContains(r.act, p.act, r.obj, p.obj)) ` ) @@ -248,20 +248,38 @@ func DBMatchFunc(args ...interface{}) (interface{}, error) { return db1 == db2, nil } +func collMatch(requestObj, policyObj string) bool { + _, coll1 := funcutil.SplitObjectName(requestObj[strings.Index(requestObj, "-")+1:]) + _, coll2 := funcutil.SplitObjectName(policyObj[strings.Index(policyObj, "-")+1:]) + + return coll2 == util.AnyWord || coll1 == coll2 +} + func PrivilegeGroupContains(args ...interface{}) (interface{}, error) { requestPrivilege := args[0].(string) policyPrivilege := args[1].(string) + requestObj := args[2].(string) + policyObj := args[3].(string) switch policyPrivilege { case commonpb.ObjectPrivilege_PrivilegeAll.String(): return true, nil case commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(): + // read only belong to collection object + if !collMatch(requestObj, policyObj) { + return false, nil + } _, ok := roPrivileges[requestPrivilege] return ok, nil case commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(): + // read write belong to collection object + if !collMatch(requestObj, policyObj) { + return false, nil + } _, ok := rwPrivileges[requestPrivilege] return ok, nil case commonpb.ObjectPrivilege_PrivilegeGroupAdmin.String(): + // admin belong to global object _, ok := adminPrivileges[requestPrivilege] return ok, nil default: diff --git a/internal/proxy/privilege_interceptor_test.go b/internal/proxy/privilege_interceptor_test.go index c7b52a1e36..785fc53191 100644 --- a/internal/proxy/privilege_interceptor_test.go +++ b/internal/proxy/privilege_interceptor_test.go @@ -235,8 +235,9 @@ func TestResourceGroupPrivilege(t *testing.T) { func TestPrivilegeGroup(t *testing.T) { ctx := context.Background() - t.Run("Read Only", func(t *testing.T) { + t.Run("grant ReadOnly to single collection", func(t *testing.T) { paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") + initPrivilegeGroups() var err error ctx = GetContext(context.Background(), "fooo:123456") @@ -248,7 +249,7 @@ func TestPrivilegeGroup(t *testing.T) { return &internalpb.ListPolicyResponse{ Status: merr.Success(), PolicyInfos: []string{ - funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Global.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"), + funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "coll1", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"), }, UserRoles: []string{ funcutil.EncodeUserRoleCache("fooo", "role1"), @@ -258,18 +259,169 @@ func TestPrivilegeGroup(t *testing.T) { InitMetaCache(ctx, client, queryCoord, mgr) defer CleanPrivilegeCache() - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll1", + }) assert.NoError(t, err) - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll1", + }) assert.NoError(t, err) - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll1", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.ShowCollectionsRequest{}) + assert.NoError(t, err) + }) + + t.Run("grant ReadOnly to all collection", func(t *testing.T) { + paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") + initPrivilegeGroups() + + var err error + ctx = GetContext(context.Background(), "fooo:123456") + client := &MockRootCoordClientInterface{} + queryCoord := &mocks.MockQueryCoordClient{} + mgr := newShardClientMgr() + + client.listPolicy = func(ctx context.Context, in *internalpb.ListPolicyRequest) (*internalpb.ListPolicyResponse, error) { + return &internalpb.ListPolicyResponse{ + Status: merr.Success(), + PolicyInfos: []string{ + funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"), + }, + UserRoles: []string{ + funcutil.EncodeUserRoleCache("fooo", "role1"), + }, + }, nil + } + InitMetaCache(ctx, client, queryCoord, mgr) + defer CleanPrivilegeCache() + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll1", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll2", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll2", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.ShowCollectionsRequest{}) + assert.NoError(t, err) + }) + + t.Run("grant ReadWrite to single collection", func(t *testing.T) { + paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") + initPrivilegeGroups() + + var err error + ctx = GetContext(context.Background(), "fooo:123456") + client := &MockRootCoordClientInterface{} + queryCoord := &mocks.MockQueryCoordClient{} + mgr := newShardClientMgr() + + client.listPolicy = func(ctx context.Context, in *internalpb.ListPolicyRequest) (*internalpb.ListPolicyResponse, error) { + return &internalpb.ListPolicyResponse{ + Status: merr.Success(), + PolicyInfos: []string{ + funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "coll1", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"), + }, + UserRoles: []string{ + funcutil.EncodeUserRoleCache("fooo", "role1"), + }, + }, nil + } + InitMetaCache(ctx, client, queryCoord, mgr) + defer CleanPrivilegeCache() + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{ + CollectionName: "coll2", + }) + assert.Error(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.CreateResourceGroupRequest{}) assert.Error(t, err) }) - t.Run("Read Write", func(t *testing.T) { + t.Run("grant ReadWrite to all collection", func(t *testing.T) { paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") + initPrivilegeGroups() var err error ctx = GetContext(context.Background(), "fooo:123456") @@ -281,7 +433,7 @@ func TestPrivilegeGroup(t *testing.T) { return &internalpb.ListPolicyResponse{ Status: merr.Success(), PolicyInfos: []string{ - funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Global.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"), + funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"), }, UserRoles: []string{ funcutil.EncodeUserRoleCache("fooo", "role1"), @@ -291,16 +443,44 @@ func TestPrivilegeGroup(t *testing.T) { InitMetaCache(ctx, client, queryCoord, mgr) defer CleanPrivilegeCache() - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll1", + }) assert.NoError(t, err) - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll1", + }) assert.NoError(t, err) - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll1", + }) assert.NoError(t, err) - _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{}) + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{ + CollectionName: "coll1", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{ + CollectionName: "coll2", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{ + CollectionName: "coll2", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{ + CollectionName: "coll2", + }) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{ + CollectionName: "coll2", + }) assert.NoError(t, err) _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.CreateResourceGroupRequest{}) @@ -309,6 +489,7 @@ func TestPrivilegeGroup(t *testing.T) { t.Run("Admin", func(t *testing.T) { paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true") + initPrivilegeGroups() var err error ctx = GetContext(context.Background(), "fooo:123456") diff --git a/internal/rootcoord/root_coord.go b/internal/rootcoord/root_coord.go index d75ba5e23a..7484b195a8 100644 --- a/internal/rootcoord/root_coord.go +++ b/internal/rootcoord/root_coord.go @@ -2524,7 +2524,7 @@ func (c *Core) isValidGrantor(entity *milvuspb.GrantorEntity, object string) err return nil } } - return fmt.Errorf("not found the privilege name[%s]", entity.Privilege.Name) + return fmt.Errorf("not found the privilege name[%s] in object[%s]", entity.Privilege.Name, object) } // OperatePrivilege operate the privilege, including grant and revoke diff --git a/pkg/util/constant.go b/pkg/util/constant.go index 3aec9b9287..4fae9f09a3 100644 --- a/pkg/util/constant.go +++ b/pkg/util/constant.go @@ -57,8 +57,9 @@ const ( NonDBID = int64(0) InvalidDBID = int64(-1) - PrivilegeWord = "Privilege" - AnyWord = "*" + PrivilegeWord = "Privilege" + PrivilegeGroupWord = "PrivilegeGroup" + AnyWord = "*" IdentifierKey = "identifier" @@ -110,6 +111,8 @@ var ( MetaStore2API(commonpb.ObjectPrivilege_PrivilegeShowPartitions.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeHasPartition.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetFlushState.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String()), }, commonpb.ObjectType_Global.String(): { MetaStore2API(commonpb.ObjectPrivilege_PrivilegeAll.String()), @@ -146,6 +149,7 @@ var ( MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropAlias.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeAlias.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListAliases.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupAdmin.String()), }, commonpb.ObjectType_User.String(): { MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()), @@ -198,8 +202,6 @@ var ( commonpb.ObjectPrivilege_PrivilegeGetStatistics.String(), commonpb.ObjectPrivilege_PrivilegeCreateIndex.String(), commonpb.ObjectPrivilege_PrivilegeDropIndex.String(), - commonpb.ObjectPrivilege_PrivilegeCreateCollection.String(), - commonpb.ObjectPrivilege_PrivilegeDropCollection.String(), commonpb.ObjectPrivilege_PrivilegeCreatePartition.String(), commonpb.ObjectPrivilege_PrivilegeDropPartition.String(), commonpb.ObjectPrivilege_PrivilegeLoad.String(), @@ -216,6 +218,8 @@ var ( commonpb.ObjectPrivilege_PrivilegeDropAlias.String(), } AdminPrivilegeGroup = []string{ + commonpb.ObjectPrivilege_PrivilegeCreateCollection.String(), + commonpb.ObjectPrivilege_PrivilegeDropCollection.String(), commonpb.ObjectPrivilege_PrivilegeQuery.String(), commonpb.ObjectPrivilege_PrivilegeSearch.String(), commonpb.ObjectPrivilege_PrivilegeIndexDetail.String(), @@ -291,7 +295,11 @@ func StringList(stringMap map[string]struct{}) []string { // MetaStore2API convert meta-store's privilege name to api's // example: PrivilegeAll -> All func MetaStore2API(name string) string { - return name[strings.Index(name, PrivilegeWord)+len(PrivilegeWord):] + prefix := PrivilegeWord + if strings.Contains(name, PrivilegeGroupWord) { + prefix = PrivilegeGroupWord + } + return name[strings.Index(name, prefix)+len(prefix):] } func PrivilegeNameForAPI(name string) string { @@ -303,10 +311,17 @@ func PrivilegeNameForAPI(name string) string { } func PrivilegeNameForMetastore(name string) string { + // check if name is single privilege dbPrivilege := PrivilegeWord + name _, ok := commonpb.ObjectPrivilege_value[dbPrivilege] if !ok { - return "" + // check if name is privilege group + dbPrivilege := PrivilegeGroupWord + name + _, ok := commonpb.ObjectPrivilege_value[dbPrivilege] + if !ok { + return "" + } + return dbPrivilege } return dbPrivilege } diff --git a/tests/integration/rbac/privilege_group_test.go b/tests/integration/rbac/privilege_group_test.go new file mode 100644 index 0000000000..11a574911a --- /dev/null +++ b/tests/integration/rbac/privilege_group_test.go @@ -0,0 +1,137 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package rbac + +import ( + "context" + "testing" + + "github.com/stretchr/testify/suite" + + "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" + "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb" + "github.com/milvus-io/milvus/pkg/util" + "github.com/milvus-io/milvus/pkg/util/merr" + "github.com/milvus-io/milvus/pkg/util/paramtable" + "github.com/milvus-io/milvus/tests/integration" +) + +type PrivilegeGroupTestSuite struct { + integration.MiniClusterSuite +} + +func (s *PrivilegeGroupTestSuite) SetupSuite() { + paramtable.Init() + paramtable.Get().Save(paramtable.Get().QueryCoordCfg.BalanceCheckInterval.Key, "1000") + paramtable.Get().Save(paramtable.Get().QueryNodeCfg.GracefulStopTimeout.Key, "1") + paramtable.Get().Save(paramtable.Get().CommonCfg.AuthorizationEnabled.Key, "true") + + s.Require().NoError(s.SetupEmbedEtcd()) +} + +func (s *PrivilegeGroupTestSuite) TestPrivilegeGroup() { + ctx := GetContext(context.Background(), "root:123456") + // test empty rbac content + resp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{}) + s.NoError(err) + s.True(merr.Ok(resp.GetStatus())) + s.Equal("", resp.GetRBACMeta().String()) + + // generate some rbac content + roleName := "test_role" + resp1, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{ + Entity: &milvuspb.RoleEntity{ + Name: roleName, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp1)) + resp2, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{ + Type: milvuspb.OperatePrivilegeType_Grant, + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: roleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()}, + ObjectName: util.AnyWord, + DbName: util.AnyWord, + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: "ReadOnly"}, + }, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp2)) + + resp3, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{ + Type: milvuspb.OperatePrivilegeType_Grant, + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: roleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()}, + ObjectName: util.AnyWord, + DbName: util.AnyWord, + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: "ReadWrite"}, + }, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp3)) + + resp4, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{ + Type: milvuspb.OperatePrivilegeType_Grant, + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: roleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: util.AnyWord, + DbName: util.AnyWord, + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: "Admin"}, + }, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp4)) + + resp5, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{ + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: roleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: util.AnyWord, + DbName: util.AnyWord, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp5.GetStatus())) + s.Len(resp5.GetEntities(), 1) + + resp6, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{ + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: roleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()}, + ObjectName: util.AnyWord, + DbName: util.AnyWord, + }, + }) + s.NoError(err) + s.True(merr.Ok(resp6.GetStatus())) + s.Len(resp6.GetEntities(), 2) +} + +func TestPrivilegeGroup(t *testing.T) { + suite.Run(t, new(PrivilegeGroupTestSuite)) +}