From 5c5948cb70ec500f8bf5b7af2cb47eee891c9202 Mon Sep 17 00:00:00 2001 From: sthuang <167743503+shaoting-huang@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:24:59 +0800 Subject: [PATCH] fix: rbac custom group privilege level check (#39164) related: https://github.com/milvus-io/milvus/issues/39086 Signed-off-by: shaoting-huang --- internal/proxy/impl.go | 4 - internal/proxy/util.go | 25 --- internal/rootcoord/meta_table.go | 4 +- internal/rootcoord/rbac_task.go | 12 +- internal/rootcoord/root_coord.go | 46 ++++- pkg/util/constant.go | 119 +++++++++++++ pkg/util/paramtable/rbac_config_test.go | 24 ++- pkg/util/paramtable/rbac_param.go | 160 +++++------------- .../integration/rbac/privilege_group_test.go | 3 +- tests/integration/rbac/rbac_backup_test.go | 19 ++- tests/python_client/testcases/test_utility.py | 4 +- 11 files changed, 235 insertions(+), 185 deletions(-) diff --git a/internal/proxy/impl.go b/internal/proxy/impl.go index 017fa52614377..fa8b64fff4d14 100644 --- a/internal/proxy/impl.go +++ b/internal/proxy/impl.go @@ -5439,10 +5439,6 @@ func (node *Proxy) validateOperatePrivilegeV2Params(req *milvuspb.OperatePrivile if err := ValidatePrivilege(req.Grantor.Privilege.Name); err != nil { return err } - // validate built-in privilege group params - if err := ValidateBuiltInPrivilegeGroup(req.Grantor.Privilege.Name, req.DbName, req.CollectionName); err != nil { - return err - } if req.Type != milvuspb.OperatePrivilegeType_Grant && req.Type != milvuspb.OperatePrivilegeType_Revoke { return merr.WrapErrParameterInvalidMsg("the type in the request not grant or revoke") } diff --git a/internal/proxy/util.go b/internal/proxy/util.go index 16e01cae8b7f1..057b58f2a01b3 100644 --- a/internal/proxy/util.go +++ b/internal/proxy/util.go @@ -1120,31 +1120,6 @@ func ValidatePrivilege(entity string) error { return validateName(entity, "Privilege") } -func ValidateBuiltInPrivilegeGroup(entity string, dbName string, collectionName string) error { - if !util.IsBuiltinPrivilegeGroup(entity) { - return nil - } - switch { - case strings.HasPrefix(entity, milvuspb.PrivilegeLevel_Cluster.String()): - if !util.IsAnyWord(dbName) || !util.IsAnyWord(collectionName) { - return merr.WrapErrParameterInvalidMsg("dbName and collectionName should be * for the cluster level privilege: %s", entity) - } - return nil - case strings.HasPrefix(entity, milvuspb.PrivilegeLevel_Database.String()): - if collectionName != "" && collectionName != util.AnyWord { - return merr.WrapErrParameterInvalidMsg("collectionName should be * for the database level privilege: %s", entity) - } - return nil - case strings.HasPrefix(entity, milvuspb.PrivilegeLevel_Collection.String()): - if util.IsAnyWord(dbName) && !util.IsAnyWord(collectionName) && collectionName != "" { - return merr.WrapErrParameterInvalidMsg("please specify database name for the collection level privilege: %s", entity) - } - return nil - default: - return nil - } -} - func GetCurUserFromContext(ctx context.Context) (string, error) { return contextutil.GetCurUserFromContext(ctx) } diff --git a/internal/rootcoord/meta_table.go b/internal/rootcoord/meta_table.go index 1de392ff1f792..02e91a4ac689c 100644 --- a/internal/rootcoord/meta_table.go +++ b/internal/rootcoord/meta_table.go @@ -1525,7 +1525,7 @@ func (mt *MetaTable) RestoreRBAC(ctx context.Context, tenant string, meta *milvu return mt.catalog.RestoreRBAC(ctx, tenant, meta) } -// check if the privielge group name is defined by users +// check if the privilege group name is defined by users func (mt *MetaTable) IsCustomPrivilegeGroup(ctx context.Context, groupName string) (bool, error) { privGroups, err := mt.catalog.ListPrivilegeGroups(ctx) if err != nil { @@ -1641,7 +1641,7 @@ func (mt *MetaTable) OperatePrivilegeGroup(ctx context.Context, groupName string if group.GroupName == p.Name { privileges = append(privileges, group.Privileges...) } else { - return merr.WrapErrParameterInvalidMsg("there is no privilege name or privielge group name [%s] defined in system to operate", p.Name) + return merr.WrapErrParameterInvalidMsg("there is no privilege name or privilege group name [%s] defined in system to operate", p.Name) } } } diff --git a/internal/rootcoord/rbac_task.go b/internal/rootcoord/rbac_task.go index 8d826cc62afdd..50cb42f965ec8 100644 --- a/internal/rootcoord/rbac_task.go +++ b/internal/rootcoord/rbac_task.go @@ -179,7 +179,7 @@ func executeOperatePrivilegeTaskSteps(ctx context.Context, core *Core, in *milvu } grants := []*milvuspb.GrantEntity{in.Entity} - allGroups, err := core.getPrivilegeGroups(ctx) + allGroups, err := core.getDefaultAndCustomPrivilegeGroups(ctx) if err != nil { return nil, err } @@ -275,12 +275,12 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in * return p.Name }) - // check if privileges are the same object type - objectTypes := lo.SliceToMap(newPrivs, func(p *milvuspb.PrivilegeEntity) (string, struct{}) { - return util.GetObjectType(p.Name), struct{}{} + // check if privileges are the same privilege level + privilegeLevels := lo.SliceToMap(newPrivs, func(p *milvuspb.PrivilegeEntity) (string, struct{}) { + return util.GetPrivilegeLevel(p.Name), struct{}{} }) - if len(objectTypes) > 1 { - return nil, errors.New("privileges are not the same object type") + if len(privilegeLevels) > 1 { + return nil, errors.New("privileges are not the same privilege level") } case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup: newPrivs, _ := lo.Difference(v, in.Privileges) diff --git a/internal/rootcoord/root_coord.go b/internal/rootcoord/root_coord.go index 7839e358da477..16e319cb8cf62 100644 --- a/internal/rootcoord/root_coord.go +++ b/internal/rootcoord/root_coord.go @@ -2595,6 +2595,10 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile ctxLog.Error("", zap.Error(err)) return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil } + if err := c.validatePrivilegeGroupParams(ctx, privName, in.Entity.DbName, in.Entity.ObjectName); err != nil { + ctxLog.Error("", zap.Error(err)) + return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil + } // set up object type for metastore, to be compatible with v1 version in.Entity.Object.Name = util.GetObjectType(privName) default: @@ -2656,6 +2660,42 @@ func (c *Core) operatePrivilegeCommonCheck(ctx context.Context, in *milvuspb.Ope return nil } +func (c *Core) validatePrivilegeGroupParams(ctx context.Context, entity string, dbName string, collectionName string) error { + allGroups, err := c.getDefaultAndCustomPrivilegeGroups(ctx) + if err != nil { + return err + } + groups := lo.SliceToMap(allGroups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) { + return group.GroupName, group.Privileges + }) + privs, exists := groups[entity] + if !exists || len(privs) == 0 { + // it is a privilege, no need to check with other params + return nil + } + // since all privileges are same level in a group, just check the first privilege + level := util.GetPrivilegeLevel(privs[0].GetName()) + switch level { + case milvuspb.PrivilegeLevel_Cluster.String(): + if !util.IsAnyWord(dbName) || !util.IsAnyWord(collectionName) { + return merr.WrapErrParameterInvalidMsg("dbName and collectionName should be * for the cluster level privilege: %s", entity) + } + return nil + case milvuspb.PrivilegeLevel_Database.String(): + if collectionName != "" && collectionName != util.AnyWord { + return merr.WrapErrParameterInvalidMsg("collectionName should be * for the database level privilege: %s", entity) + } + return nil + case milvuspb.PrivilegeLevel_Collection.String(): + if util.IsAnyWord(dbName) && !util.IsAnyWord(collectionName) && collectionName != "" { + return merr.WrapErrParameterInvalidMsg("please specify database name for the collection level privilege: %s", entity) + } + return nil + default: + return errors.New("not found the privilege level") + } +} + func (c *Core) getMetastorePrivilegeName(ctx context.Context, privName string) (string, error) { // if it is built-in privilege, return the privilege name directly if util.IsPrivilegeNameDefined(privName) { @@ -2757,7 +2797,7 @@ func (c *Core) ListPolicy(ctx context.Context, in *internalpb.ListPolicyRequest) }, nil } // expand privilege groups and turn to policies - allGroups, err := c.getPrivilegeGroups(ctx) + allGroups, err := c.getDefaultAndCustomPrivilegeGroups(ctx) if err != nil { errMsg := "fail to get privilege groups" ctxLog.Warn(errMsg, zap.Error(err)) @@ -3131,8 +3171,8 @@ func (c *Core) expandPrivilegeGroups(ctx context.Context, grants []*milvuspb.Gra }), nil } -// getPrivilegeGroups returns default privilege groups and user-defined privilege groups. -func (c *Core) getPrivilegeGroups(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) { +// getDefaultAndCustomPrivilegeGroups returns default privilege groups and user-defined privilege groups. +func (c *Core) getDefaultAndCustomPrivilegeGroups(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) { allGroups, err := c.meta.ListPrivilegeGroups(ctx) allGroups = append(allGroups, Params.RbacConfig.GetDefaultPrivilegeGroups()...) if err != nil { diff --git a/pkg/util/constant.go b/pkg/util/constant.go index d1ca65d396a43..70100bfbf17b6 100644 --- a/pkg/util/constant.go +++ b/pkg/util/constant.go @@ -22,6 +22,7 @@ import ( "github.com/samber/lo" "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/common" "github.com/milvus-io/milvus/pkg/util/typeutil" ) @@ -292,6 +293,124 @@ var ( } ) +// rbac v2 uses privilege level to group privileges rather than object type +var ( + CollectionReadOnlyPrivileges = ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeQuery.String(), + commonpb.ObjectPrivilege_PrivilegeSearch.String(), + commonpb.ObjectPrivilege_PrivilegeIndexDetail.String(), + commonpb.ObjectPrivilege_PrivilegeGetFlushState.String(), + commonpb.ObjectPrivilege_PrivilegeGetLoadState.String(), + commonpb.ObjectPrivilege_PrivilegeGetLoadingProgress.String(), + commonpb.ObjectPrivilege_PrivilegeHasPartition.String(), + commonpb.ObjectPrivilege_PrivilegeShowPartitions.String(), + commonpb.ObjectPrivilege_PrivilegeDescribeCollection.String(), + commonpb.ObjectPrivilege_PrivilegeDescribeAlias.String(), + commonpb.ObjectPrivilege_PrivilegeGetStatistics.String(), + commonpb.ObjectPrivilege_PrivilegeListAliases.String(), + }) + + CollectionReadWritePrivileges = append(CollectionReadOnlyPrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeLoad.String(), + commonpb.ObjectPrivilege_PrivilegeRelease.String(), + commonpb.ObjectPrivilege_PrivilegeInsert.String(), + commonpb.ObjectPrivilege_PrivilegeDelete.String(), + commonpb.ObjectPrivilege_PrivilegeUpsert.String(), + commonpb.ObjectPrivilege_PrivilegeImport.String(), + commonpb.ObjectPrivilege_PrivilegeFlush.String(), + commonpb.ObjectPrivilege_PrivilegeCompaction.String(), + commonpb.ObjectPrivilege_PrivilegeLoadBalance.String(), + commonpb.ObjectPrivilege_PrivilegeCreateIndex.String(), + commonpb.ObjectPrivilege_PrivilegeDropIndex.String(), + commonpb.ObjectPrivilege_PrivilegeCreatePartition.String(), + commonpb.ObjectPrivilege_PrivilegeDropPartition.String(), + })..., + ) + + CollectionAdminPrivileges = append(CollectionReadWritePrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeCreateAlias.String(), + commonpb.ObjectPrivilege_PrivilegeDropAlias.String(), + })..., + ) + + DatabaseReadOnlyPrivileges = ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeShowCollections.String(), + commonpb.ObjectPrivilege_PrivilegeDescribeDatabase.String(), + }) + + DatabaseReadWritePrivileges = append(DatabaseReadOnlyPrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeAlterDatabase.String(), + })..., + ) + + DatabaseAdminPrivileges = append(DatabaseReadWritePrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeCreateCollection.String(), + commonpb.ObjectPrivilege_PrivilegeDropCollection.String(), + })..., + ) + + ClusterReadOnlyPrivileges = ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeListDatabases.String(), + commonpb.ObjectPrivilege_PrivilegeSelectOwnership.String(), + commonpb.ObjectPrivilege_PrivilegeSelectUser.String(), + commonpb.ObjectPrivilege_PrivilegeDescribeResourceGroup.String(), + commonpb.ObjectPrivilege_PrivilegeListResourceGroups.String(), + commonpb.ObjectPrivilege_PrivilegeListPrivilegeGroups.String(), + }) + + ClusterReadWritePrivileges = append(ClusterReadOnlyPrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeFlushAll.String(), + commonpb.ObjectPrivilege_PrivilegeTransferNode.String(), + commonpb.ObjectPrivilege_PrivilegeTransferReplica.String(), + commonpb.ObjectPrivilege_PrivilegeUpdateResourceGroups.String(), + })..., + ) + + ClusterAdminPrivileges = append(ClusterReadWritePrivileges, + ConvertPrivileges([]string{ + commonpb.ObjectPrivilege_PrivilegeBackupRBAC.String(), + commonpb.ObjectPrivilege_PrivilegeRestoreRBAC.String(), + commonpb.ObjectPrivilege_PrivilegeCreateDatabase.String(), + commonpb.ObjectPrivilege_PrivilegeDropDatabase.String(), + commonpb.ObjectPrivilege_PrivilegeCreateOwnership.String(), + commonpb.ObjectPrivilege_PrivilegeDropOwnership.String(), + commonpb.ObjectPrivilege_PrivilegeManageOwnership.String(), + commonpb.ObjectPrivilege_PrivilegeCreateResourceGroup.String(), + commonpb.ObjectPrivilege_PrivilegeDropResourceGroup.String(), + commonpb.ObjectPrivilege_PrivilegeUpdateUser.String(), + commonpb.ObjectPrivilege_PrivilegeRenameCollection.String(), + commonpb.ObjectPrivilege_PrivilegeCreatePrivilegeGroup.String(), + commonpb.ObjectPrivilege_PrivilegeDropPrivilegeGroup.String(), + commonpb.ObjectPrivilege_PrivilegeOperatePrivilegeGroup.String(), + })..., + ) +) + +// ConvertPrivileges converts each privilege from metastore format to API format. +func ConvertPrivileges(privileges []string) []string { + return lo.Map(privileges, func(name string, _ int) string { + return MetaStore2API(name) + }) +} + +func GetPrivilegeLevel(privilege string) string { + if lo.Contains(ClusterAdminPrivileges, privilege) { + return milvuspb.PrivilegeLevel_Cluster.String() + } + if lo.Contains(DatabaseAdminPrivileges, privilege) { + return milvuspb.PrivilegeLevel_Database.String() + } + if lo.Contains(CollectionAdminPrivileges, privilege) { + return milvuspb.PrivilegeLevel_Collection.String() + } + return "" +} + // StringSet convert array to map for conveniently check if the array contains an element func StringSet(strings []string) map[string]struct{} { stringsMap := make(map[string]struct{}) diff --git a/pkg/util/paramtable/rbac_config_test.go b/pkg/util/paramtable/rbac_config_test.go index 9a780bcea435b..c7519d4e1eed1 100644 --- a/pkg/util/paramtable/rbac_config_test.go +++ b/pkg/util/paramtable/rbac_config_test.go @@ -22,26 +22,24 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRbacConfig_Init(t *testing.T) { +func TestRbacConfig_DefaultPrivileges(t *testing.T) { params := ComponentParam{} params.Init(NewBaseTable(SkipRemote(true))) cfg := ¶ms.RbacConfig assert.Equal(t, len(cfg.GetDefaultPrivilegeGroupNames()), 9) - assert.True(t, cfg.IsCollectionPrivilegeGroup("CollectionReadOnly")) - assert.False(t, cfg.IsCollectionPrivilegeGroup("DatabaseReadOnly")) assert.Equal(t, cfg.Enabled.GetAsBool(), false) - assert.Equal(t, cfg.ClusterReadOnlyPrivileges.GetAsStrings(), builtinPrivilegeGroups["ClusterReadOnly"]) - assert.Equal(t, cfg.ClusterReadWritePrivileges.GetAsStrings(), builtinPrivilegeGroups["ClusterReadWrite"]) - assert.Equal(t, cfg.ClusterAdminPrivileges.GetAsStrings(), builtinPrivilegeGroups["ClusterAdmin"]) - assert.Equal(t, cfg.DBReadOnlyPrivileges.GetAsStrings(), builtinPrivilegeGroups["DatabaseReadOnly"]) - assert.Equal(t, cfg.DBReadWritePrivileges.GetAsStrings(), builtinPrivilegeGroups["DatabaseReadWrite"]) - assert.Equal(t, cfg.DBAdminPrivileges.GetAsStrings(), builtinPrivilegeGroups["DatabaseAdmin"]) - assert.Equal(t, cfg.CollectionReadOnlyPrivileges.GetAsStrings(), builtinPrivilegeGroups["CollectionReadOnly"]) - assert.Equal(t, cfg.CollectionReadWritePrivileges.GetAsStrings(), builtinPrivilegeGroups["CollectionReadWrite"]) - assert.Equal(t, cfg.CollectionAdminPrivileges.GetAsStrings(), builtinPrivilegeGroups["CollectionAdmin"]) + assert.Equal(t, cfg.ClusterReadOnlyPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("ClusterReadOnly")) + assert.Equal(t, cfg.ClusterReadWritePrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("ClusterReadWrite")) + assert.Equal(t, cfg.ClusterAdminPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("ClusterAdmin")) + assert.Equal(t, cfg.DBReadOnlyPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("DatabaseReadOnly")) + assert.Equal(t, cfg.DBReadWritePrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("DatabaseReadWrite")) + assert.Equal(t, cfg.DBAdminPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("DatabaseAdmin")) + assert.Equal(t, cfg.CollectionReadOnlyPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("CollectionReadOnly")) + assert.Equal(t, cfg.CollectionReadWritePrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("CollectionReadWrite")) + assert.Equal(t, cfg.CollectionAdminPrivileges.GetAsStrings(), cfg.GetDefaultPrivilegeGroupPrivileges("CollectionAdmin")) } -func TestRbacConfig_Override(t *testing.T) { +func TestRbacConfig_OverridePrivileges(t *testing.T) { params := ComponentParam{} params.Init(NewBaseTable(SkipRemote(true))) diff --git a/pkg/util/paramtable/rbac_param.go b/pkg/util/paramtable/rbac_param.go index d793a4d93b26d..35816a5d8a09f 100644 --- a/pkg/util/paramtable/rbac_param.go +++ b/pkg/util/paramtable/rbac_param.go @@ -10,103 +10,6 @@ import ( "github.com/milvus-io/milvus/pkg/util" ) -var ( - builtinPrivilegeGroups = map[string][]string{ - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionReadOnly.String()): collectionReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionReadWrite.String()): collectionReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionAdmin.String()): collectionAdminPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseReadOnly.String()): databaseReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseReadWrite.String()): databaseReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseAdmin.String()): databaseAdminPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadOnly.String()): clusterReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadWrite.String()): clusterReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterAdmin.String()): clusterAdminPrivilegeGroup, - } - - collectionReadOnlyPrivilegeGroup = []string{ - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeQuery.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeSearch.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeIndexDetail.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetFlushState.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetLoadState.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetLoadingProgress.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeHasPartition.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeShowPartitions.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeCollection.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeAlias.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetStatistics.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListAliases.String()), - } - - collectionReadWritePrivilegeGroup = append(collectionReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeLoad.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeRelease.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeInsert.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDelete.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpsert.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeImport.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeFlush.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCompaction.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeLoadBalance.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateIndex.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropIndex.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreatePartition.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropPartition.String()), - ) - - collectionAdminPrivilegeGroup = append(collectionReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateAlias.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropAlias.String()), - ) - - databaseReadOnlyPrivilegeGroup = []string{ - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeShowCollections.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeDatabase.String()), - } - - databaseReadWritePrivilegeGroup = append(databaseReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeAlterDatabase.String()), - ) - - databaseAdminPrivilegeGroup = append(databaseReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateCollection.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropCollection.String()), - ) - - clusterReadOnlyPrivilegeGroup = []string{ - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListDatabases.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeSelectOwnership.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeSelectUser.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeResourceGroup.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListResourceGroups.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListPrivilegeGroups.String()), - } - - clusterReadWritePrivilegeGroup = append(clusterReadOnlyPrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeFlushAll.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeTransferNode.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeTransferReplica.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateResourceGroups.String()), - ) - - clusterAdminPrivilegeGroup = append(clusterReadWritePrivilegeGroup, - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeBackupRBAC.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeRestoreRBAC.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateDatabase.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropDatabase.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateOwnership.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropOwnership.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeManageOwnership.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateResourceGroup.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropResourceGroup.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeRenameCollection.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreatePrivilegeGroup.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropPrivilegeGroup.String()), - util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeOperatePrivilegeGroup.String()), - ) -) - type rbacConfig struct { Enabled ParamItem `refreshable:"false"` ClusterReadOnlyPrivileges ParamItem `refreshable:"false"` @@ -134,7 +37,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.ClusterReadOnlyPrivileges = ParamItem{ Key: "common.security.rbac.cluster.readonly.privileges", - DefaultValue: strings.Join(clusterReadOnlyPrivilegeGroup, ","), + DefaultValue: strings.Join(util.ClusterReadOnlyPrivileges, ","), Version: "2.4.16", Doc: "Cluster level readonly privileges", Export: true, @@ -143,7 +46,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.ClusterReadWritePrivileges = ParamItem{ Key: "common.security.rbac.cluster.readwrite.privileges", - DefaultValue: strings.Join(clusterReadWritePrivilegeGroup, ","), + DefaultValue: strings.Join(util.ClusterReadWritePrivileges, ","), Version: "2.4.16", Doc: "Cluster level readwrite privileges", Export: true, @@ -152,7 +55,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.ClusterAdminPrivileges = ParamItem{ Key: "common.security.rbac.cluster.admin.privileges", - DefaultValue: strings.Join(clusterAdminPrivilegeGroup, ","), + DefaultValue: strings.Join(util.ClusterAdminPrivileges, ","), Version: "2.4.16", Doc: "Cluster level admin privileges", Export: true, @@ -161,7 +64,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.DBReadOnlyPrivileges = ParamItem{ Key: "common.security.rbac.database.readonly.privileges", - DefaultValue: strings.Join(databaseReadOnlyPrivilegeGroup, ","), + DefaultValue: strings.Join(util.DatabaseReadOnlyPrivileges, ","), Version: "2.4.16", Doc: "Database level readonly privileges", Export: true, @@ -170,7 +73,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.DBReadWritePrivileges = ParamItem{ Key: "common.security.rbac.database.readwrite.privileges", - DefaultValue: strings.Join(databaseReadWritePrivilegeGroup, ","), + DefaultValue: strings.Join(util.DatabaseReadWritePrivileges, ","), Version: "2.4.16", Doc: "Database level readwrite privileges", Export: true, @@ -179,7 +82,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.DBAdminPrivileges = ParamItem{ Key: "common.security.rbac.database.admin.privileges", - DefaultValue: strings.Join(databaseAdminPrivilegeGroup, ","), + DefaultValue: strings.Join(util.DatabaseAdminPrivileges, ","), Version: "2.4.16", Doc: "Database level admin privileges", Export: true, @@ -188,7 +91,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.CollectionReadOnlyPrivileges = ParamItem{ Key: "common.security.rbac.collection.readonly.privileges", - DefaultValue: strings.Join(collectionReadOnlyPrivilegeGroup, ","), + DefaultValue: strings.Join(util.CollectionReadOnlyPrivileges, ","), Version: "2.4.16", Doc: "Collection level readonly privileges", Export: true, @@ -197,7 +100,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.CollectionReadWritePrivileges = ParamItem{ Key: "common.security.rbac.collection.readwrite.privileges", - DefaultValue: strings.Join(collectionReadWritePrivilegeGroup, ","), + DefaultValue: strings.Join(util.CollectionReadWritePrivileges, ","), Version: "2.4.16", Doc: "Collection level readwrite privileges", Export: true, @@ -206,7 +109,7 @@ func (p *rbacConfig) init(base *BaseTable) { p.CollectionAdminPrivileges = ParamItem{ Key: "common.security.rbac.collection.admin.privileges", - DefaultValue: strings.Join(collectionAdminPrivilegeGroup, ","), + DefaultValue: strings.Join(util.CollectionAdminPrivileges, ","), Version: "2.4.16", Doc: "Collection level admin privileges", Export: true, @@ -219,15 +122,15 @@ func (p *rbacConfig) GetDefaultPrivilegeGroups() []*milvuspb.PrivilegeGroupInfo GroupName string Privileges func() []string }{ - {"ClusterReadOnly", p.ClusterReadOnlyPrivileges.GetAsStrings}, - {"ClusterReadWrite", p.ClusterReadWritePrivileges.GetAsStrings}, - {"ClusterAdmin", p.ClusterAdminPrivileges.GetAsStrings}, - {"DatabaseReadOnly", p.DBReadOnlyPrivileges.GetAsStrings}, - {"DatabaseReadWrite", p.DBReadWritePrivileges.GetAsStrings}, - {"DatabaseAdmin", p.DBAdminPrivileges.GetAsStrings}, - {"CollectionReadOnly", p.CollectionReadOnlyPrivileges.GetAsStrings}, - {"CollectionReadWrite", p.CollectionReadWritePrivileges.GetAsStrings}, - {"CollectionAdmin", p.CollectionAdminPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadOnly.String()), p.ClusterReadOnlyPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadWrite.String()), p.ClusterReadWritePrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterAdmin.String()), p.ClusterAdminPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseReadOnly.String()), p.DBReadOnlyPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseReadWrite.String()), p.DBReadWritePrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseAdmin.String()), p.DBAdminPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionReadOnly.String()), p.CollectionReadOnlyPrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionReadWrite.String()), p.CollectionReadWritePrivileges.GetAsStrings}, + {util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionAdmin.String()), p.CollectionAdminPrivileges.GetAsStrings}, } builtinGroups := make([]*milvuspb.PrivilegeGroupInfo, 0, len(privilegeGroupConfigs)) @@ -245,21 +148,34 @@ func (p *rbacConfig) GetDefaultPrivilegeGroups() []*milvuspb.PrivilegeGroupInfo func (p *rbacConfig) GetDefaultPrivilegeGroup(privName string) *milvuspb.PrivilegeGroupInfo { for _, group := range p.GetDefaultPrivilegeGroups() { - if group.GroupName == privName { + if group.GetGroupName() == privName { return group } } return nil } +func (p *rbacConfig) GetDefaultPrivilegeGroupPrivileges(groupName string) []string { + group := p.GetDefaultPrivilegeGroup(groupName) + if group == nil { + return nil + } + return lo.Map(group.GetPrivileges(), func(priv *milvuspb.PrivilegeEntity, _ int) string { + return priv.GetName() + }) +} + func (p *rbacConfig) GetDefaultPrivilegeGroupNames() []string { - return lo.Keys(builtinPrivilegeGroups) + return lo.Map(p.GetDefaultPrivilegeGroups(), func(group *milvuspb.PrivilegeGroupInfo, _ int) string { + return group.GroupName + }) } func (p *rbacConfig) IsCollectionPrivilegeGroup(privName string) bool { - collectionPrivilegeGroups := lo.PickBy(builtinPrivilegeGroups, func(groupName string, _ []string) bool { - return strings.Contains(groupName, "Collection") - }) - _, exists := collectionPrivilegeGroups[privName] - return exists + for _, groupName := range p.GetDefaultPrivilegeGroupNames() { + if strings.Contains(groupName, milvuspb.PrivilegeLevel_Collection.String()) && groupName == privName { + return true + } + } + return false } diff --git a/tests/integration/rbac/privilege_group_test.go b/tests/integration/rbac/privilege_group_test.go index 966eb6b55b9c5..43b1fae34fad4 100644 --- a/tests/integration/rbac/privilege_group_test.go +++ b/tests/integration/rbac/privilege_group_test.go @@ -268,7 +268,8 @@ func (s *PrivilegeGroupTestSuite) TestGrantV2CustomPrivilegeGroup() { selectResp, _ = s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), util.AnyWord, util.AnyWord) s.Len(selectResp.GetEntities(), 2) - // add different object type privileges to group1 is not allowed + // add different privilege group level privileges to group1 is not allowed + s.Equal(milvuspb.PrivilegeLevel_Database.String(), util.GetPrivilegeLevel("CreateCollection")) resp, _ = s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ GroupName: "group1", Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup, diff --git a/tests/integration/rbac/rbac_backup_test.go b/tests/integration/rbac/rbac_backup_test.go index ce526321da654..9ef45878a7f84 100644 --- a/tests/integration/rbac/rbac_backup_test.go +++ b/tests/integration/rbac/rbac_backup_test.go @@ -104,9 +104,9 @@ func (s *RBACBackupTestSuite) TestBackup() { createRole(roleName) // grant collection level search privilege to role test_role - operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + operatePrivilege(roleName, "Search", util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Grant) - // create privielge group test_group + // create privilege group test_group groupName := "test_group" createPrivGroupResp, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ GroupName: groupName, @@ -114,6 +114,11 @@ func (s *RBACBackupTestSuite) TestBackup() { s.NoError(err) s.True(merr.Ok(createPrivGroupResp)) + collectionPrivileges := []*milvuspb.PrivilegeEntity{{Name: "Query"}, {Name: "Insert"}} + for _, p := range collectionPrivileges { + s.Equal(milvuspb.PrivilegeLevel_Collection.String(), util.GetPrivilegeLevel(p.Name)) + } + // add query and insert privilege to group test_group addPrivsToGroupResp, err := s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ GroupName: groupName, @@ -124,7 +129,7 @@ func (s *RBACBackupTestSuite) TestBackup() { s.True(merr.Ok(addPrivsToGroupResp)) // grant privilege group test_group to role test_role - operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + operatePrivilege(roleName, groupName, util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Grant) userName := "test_user" passwd := "test_passwd" @@ -166,10 +171,10 @@ func (s *RBACBackupTestSuite) TestBackup() { s.False(merr.Ok(restoreRBACResp)) // revoke privilege search from role test_role before dropping the role - operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + operatePrivilege(roleName, "Search", util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Revoke) // revoke privilege group test_group from role test_role before dropping the role - operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + operatePrivilege(roleName, groupName, util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Revoke) // drop privilege group test_group dropPrivGroupResp, err := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ @@ -206,9 +211,9 @@ func (s *RBACBackupTestSuite) TestBackup() { s.Equal(backupRBACResp2.GetRBACMeta().String(), backupRBACResp.GetRBACMeta().String()) // clean rbac meta - operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + operatePrivilege(roleName, "Search", util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Revoke) - operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + operatePrivilege(roleName, groupName, util.AnyWord, util.DefaultDBName, milvuspb.OperatePrivilegeType_Revoke) dropPrivGroupResp2, err := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ GroupName: groupName, diff --git a/tests/python_client/testcases/test_utility.py b/tests/python_client/testcases/test_utility.py index 4608208499d78..9a3c7f3eaa423 100644 --- a/tests/python_client/testcases/test_utility.py +++ b/tests/python_client/testcases/test_utility.py @@ -5384,7 +5384,7 @@ def test_add_privileges_to_group_with_privilege_invalid_value(self, host, port): name = "privilege_group_1" self.utility_wrap.create_privilege_group(privilege_group=name) privilege_name = "invalid_privilege" - error = {"err_code": 1100, "err_msg": f"there is no privilege name or privielge group name [{privilege_name}] " + error = {"err_code": 1100, "err_msg": f"there is no privilege name or privilege group name [{privilege_name}] " f"defined in system to operate: invalid parameter"} self.utility_wrap.add_privileges_to_group(privilege_group="privilege_group_1", privileges=[privilege_name], check_task=CheckTasks.err_res, check_items=error) @@ -5483,7 +5483,7 @@ def test_remove_privileges_to_group_with_privilege_invalid_value(self, host, por name = "privilege_group_1" self.utility_wrap.create_privilege_group(privilege_group=name) privilege_name = "invalid_privilege" - error = {"err_code": 1100, "err_msg": f"there is no privilege name or privielge group name [{privilege_name}] " + error = {"err_code": 1100, "err_msg": f"there is no privilege name or privilege group name [{privilege_name}] " f"defined in system to operate: invalid parameter"} self.utility_wrap.remove_privileges_from_group(privilege_group="privilege_group_1", privileges=[privilege_name], check_task=CheckTasks.err_res, check_items=error)