diff --git a/go.mod b/go.mod index 0b6c7c89aa..63da99beea 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/klauspost/compress v1.17.9 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d - github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c + github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69 github.com/minio/minio-go/v7 v7.0.73 github.com/pingcap/log v1.1.1-0.20221015072633-39906604fb81 github.com/prometheus/client_golang v1.14.0 diff --git a/go.sum b/go.sum index c91589f7f5..8542171ec5 100644 --- a/go.sum +++ b/go.sum @@ -628,8 +628,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20240722103217-b7dee0e50119 h1:9VXijWu github.com/milvus-io/cgosymbolizer v0.0.0-20240722103217-b7dee0e50119/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4= -github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c h1:Ay5w6sTE1QxCydCqqW5N44EcJrMqaqbL5zcp2vclkOw= -github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69 h1:Qt0Bv2Fum3EX3OlkuQYHJINBzeU4oEuHy2lXSfB/gZw= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= github.com/milvus-io/pulsar-client-go v0.12.1 h1:O2JZp1tsYiO7C0MQ4hrUY/aJXnn2Gry6hpm7UodghmE= github.com/milvus-io/pulsar-client-go v0.12.1/go.mod h1:dkutuH4oS2pXiGm+Ti7fQZ4MRjrMPZ8IJeEGAWMeckk= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= diff --git a/internal/distributed/proxy/httpserver/constant.go b/internal/distributed/proxy/httpserver/constant.go index 73cc91025a..2dc24a634b 100644 --- a/internal/distributed/proxy/httpserver/constant.go +++ b/internal/distributed/proxy/httpserver/constant.go @@ -59,6 +59,8 @@ const ( RevokeRoleAction = "revoke_role" GrantPrivilegeAction = "grant_privilege" RevokePrivilegeAction = "revoke_privilege" + GrantPrivilegeActionV2 = "grant_privilege_v2" + RevokePrivilegeActionV2 = "revoke_privilege_v2" AlterAction = "alter" GetProgressAction = "get_progress" // deprecated, keep it for compatibility, use `/v2/vectordb/jobs/import/describe` instead AddPrivilegesToGroupAction = "add_privileges_to_group" diff --git a/internal/distributed/proxy/httpserver/handler_v2.go b/internal/distributed/proxy/httpserver/handler_v2.go index f8e7fb3bb8..9cdb44c5ba 100644 --- a/internal/distributed/proxy/httpserver/handler_v2.go +++ b/internal/distributed/proxy/httpserver/handler_v2.go @@ -147,6 +147,8 @@ func (h *HandlersV2) RegisterRoutesToV2(router gin.IRouter) { router.POST(RoleCategory+DropAction, timeoutMiddleware(wrapperPost(func() any { return &RoleReq{} }, wrapperTraceLog(h.dropRole)))) router.POST(RoleCategory+GrantPrivilegeAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.addPrivilegeToRole)))) router.POST(RoleCategory+RevokePrivilegeAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.removePrivilegeFromRole)))) + router.POST(RoleCategory+GrantPrivilegeActionV2, timeoutMiddleware(wrapperPost(func() any { return &GrantV2Req{} }, wrapperTraceLog(h.grantV2)))) + router.POST(RoleCategory+RevokePrivilegeActionV2, timeoutMiddleware(wrapperPost(func() any { return &GrantV2Req{} }, wrapperTraceLog(h.revokeV2)))) // privilege group router.POST(PrivilegeGroupCategory+CreateAction, timeoutMiddleware(wrapperPost(func() any { return &PrivilegeGroupReq{} }, wrapperTraceLog(h.createPrivilegeGroup)))) @@ -1810,6 +1812,37 @@ func (h *HandlersV2) operatePrivilegeToRole(ctx context.Context, c *gin.Context, return resp, err } +func (h *HandlersV2) operatePrivilegeToRoleV2(ctx context.Context, c *gin.Context, httpReq *GrantV2Req, operateType milvuspb.OperatePrivilegeType) (interface{}, error) { + req := &milvuspb.OperatePrivilegeRequest{ + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: httpReq.RoleName}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: httpReq.CollectionName, + DbName: httpReq.DbName, + Grantor: &milvuspb.GrantorEntity{ + Privilege: &milvuspb.PrivilegeEntity{Name: httpReq.Privilege}, + }, + }, + Type: operateType, + Version: "v2", + } + resp, err := wrapperProxy(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/OperatePrivilege", func(reqCtx context.Context, req any) (interface{}, error) { + return h.proxy.OperatePrivilege(reqCtx, req.(*milvuspb.OperatePrivilegeRequest)) + }) + if err == nil { + HTTPReturn(c, http.StatusOK, wrapperReturnDefault()) + } + return resp, err +} + +func (h *HandlersV2) grantV2(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) { + return h.operatePrivilegeToRoleV2(ctx, c, anyReq.(*GrantV2Req), milvuspb.OperatePrivilegeType_Grant) +} + +func (h *HandlersV2) revokeV2(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) { + return h.operatePrivilegeToRoleV2(ctx, c, anyReq.(*GrantV2Req), milvuspb.OperatePrivilegeType_Revoke) +} + func (h *HandlersV2) addPrivilegeToRole(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) { return h.operatePrivilegeToRole(ctx, c, anyReq.(*GrantReq), milvuspb.OperatePrivilegeType_Grant, dbName) } diff --git a/internal/distributed/proxy/httpserver/handler_v2_test.go b/internal/distributed/proxy/httpserver/handler_v2_test.go index 74584b8a70..6658f86b18 100644 --- a/internal/distributed/proxy/httpserver/handler_v2_test.go +++ b/internal/distributed/proxy/httpserver/handler_v2_test.go @@ -1454,7 +1454,7 @@ func TestMethodPost(t *testing.T) { mp.EXPECT().UpdateCredential(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Once() mp.EXPECT().OperateUserRole(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Twice() mp.EXPECT().CreateRole(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Once() - mp.EXPECT().OperatePrivilege(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Twice() + mp.EXPECT().OperatePrivilege(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Times(4) mp.EXPECT().CreateIndex(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Twice() mp.EXPECT().CreateIndex(mock.Anything, mock.Anything).Return(commonErrorStatus, nil).Once() mp.EXPECT().CreateAlias(mock.Anything, mock.Anything).Return(commonSuccessStatus, nil).Once() @@ -1527,6 +1527,12 @@ func TestMethodPost(t *testing.T) { queryTestCases = append(queryTestCases, rawTestCase{ path: versionalV2(RoleCategory, RevokePrivilegeAction), }) + queryTestCases = append(queryTestCases, rawTestCase{ + path: versionalV2(RoleCategory, GrantPrivilegeActionV2), + }) + queryTestCases = append(queryTestCases, rawTestCase{ + path: versionalV2(RoleCategory, RevokePrivilegeActionV2), + }) queryTestCases = append(queryTestCases, rawTestCase{ path: versionalV2(IndexCategory, CreateAction), }) diff --git a/internal/distributed/proxy/httpserver/request_v2.go b/internal/distributed/proxy/httpserver/request_v2.go index f90ef1b69c..54c77bd75c 100644 --- a/internal/distributed/proxy/httpserver/request_v2.go +++ b/internal/distributed/proxy/httpserver/request_v2.go @@ -294,6 +294,13 @@ type PrivilegeGroupReq struct { Privileges []string `json:"privileges"` } +type GrantV2Req struct { + RoleName string `json:"roleName" binding:"required"` + DbName string `json:"dbName"` + CollectionName string `json:"collectionName"` + Privilege string `json:"privilege" binding:"required"` +} + type GrantReq struct { RoleName string `json:"roleName" binding:"required"` ObjectType string `json:"objectType" binding:"required"` diff --git a/internal/mocks/mock_proxy.go b/internal/mocks/mock_proxy.go index d02c995718..28b1f98154 100644 --- a/internal/mocks/mock_proxy.go +++ b/internal/mocks/mock_proxy.go @@ -5137,6 +5137,65 @@ func (_c *MockProxy_OperatePrivilegeGroup_Call) RunAndReturn(run func(context.Co return _c } +// OperatePrivilegeV2 provides a mock function with given fields: _a0, _a1 +func (_m *MockProxy) OperatePrivilegeV2(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeV2Request) (*commonpb.Status, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for OperatePrivilegeV2") + } + + var r0 *commonpb.Status + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeV2Request) (*commonpb.Status, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeV2Request) *commonpb.Status); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*commonpb.Status) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.OperatePrivilegeV2Request) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockProxy_OperatePrivilegeV2_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatePrivilegeV2' +type MockProxy_OperatePrivilegeV2_Call struct { + *mock.Call +} + +// OperatePrivilegeV2 is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *milvuspb.OperatePrivilegeV2Request +func (_e *MockProxy_Expecter) OperatePrivilegeV2(_a0 interface{}, _a1 interface{}) *MockProxy_OperatePrivilegeV2_Call { + return &MockProxy_OperatePrivilegeV2_Call{Call: _e.mock.On("OperatePrivilegeV2", _a0, _a1)} +} + +func (_c *MockProxy_OperatePrivilegeV2_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeV2Request)) *MockProxy_OperatePrivilegeV2_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*milvuspb.OperatePrivilegeV2Request)) + }) + return _c +} + +func (_c *MockProxy_OperatePrivilegeV2_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_OperatePrivilegeV2_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockProxy_OperatePrivilegeV2_Call) RunAndReturn(run func(context.Context, *milvuspb.OperatePrivilegeV2Request) (*commonpb.Status, error)) *MockProxy_OperatePrivilegeV2_Call { + _c.Call.Return(run) + return _c +} + // OperateUserRole provides a mock function with given fields: _a0, _a1 func (_m *MockProxy) OperateUserRole(_a0 context.Context, _a1 *milvuspb.OperateUserRoleRequest) (*commonpb.Status, error) { ret := _m.Called(_a0, _a1) diff --git a/internal/proxy/impl.go b/internal/proxy/impl.go index 9ece14dac7..f889b517af 100644 --- a/internal/proxy/impl.go +++ b/internal/proxy/impl.go @@ -5332,6 +5332,87 @@ func (node *Proxy) validPrivilegeParams(req *milvuspb.OperatePrivilegeRequest) e return nil } +func (node *Proxy) validOperatePrivilegeV2Params(req *milvuspb.OperatePrivilegeV2Request) error { + if req.Role == nil { + return fmt.Errorf("the role in the request is nil") + } + if err := ValidateRoleName(req.Role.Name); err != nil { + return err + } + if err := ValidatePrivilege(req.Grantor.Privilege.Name); err != nil { + return err + } + if req.Type != milvuspb.OperatePrivilegeType_Grant && req.Type != milvuspb.OperatePrivilegeType_Revoke { + return fmt.Errorf("the type in the request not grant or revoke") + } + if err := ValidateObjectName(req.DbName); err != nil { + return err + } + if err := ValidateObjectName(req.CollectionName); err != nil { + return err + } + return nil +} + +func (node *Proxy) OperatePrivilegeV2(ctx context.Context, req *milvuspb.OperatePrivilegeV2Request) (*commonpb.Status, error) { + ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-OperatePrivilegeV2") + defer sp.End() + + log := log.Ctx(ctx) + + log.Info("OperatePrivilegeV2", + zap.Any("req", req)) + if err := merr.CheckHealthy(node.GetStateCode()); err != nil { + return merr.Status(err), nil + } + if err := node.validOperatePrivilegeV2Params(req); err != nil { + return merr.Status(err), nil + } + curUser, err := GetCurUserFromContext(ctx) + if err != nil { + return merr.Status(err), nil + } + req.Grantor.User = &milvuspb.UserEntity{Name: curUser} + request := &milvuspb.OperatePrivilegeRequest{ + Base: &commonpb.MsgBase{MsgType: commonpb.MsgType_OperatePrivilege}, + Entity: &milvuspb.GrantEntity{ + Role: req.Role, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: req.CollectionName, + DbName: req.DbName, + Grantor: req.Grantor, + }, + Type: req.Type, + Version: "v2", + } + req.Grantor.User = &milvuspb.UserEntity{Name: curUser} + result, err := node.rootCoord.OperatePrivilege(ctx, request) + if err != nil { + log.Warn("fail to operate privilege", zap.Error(err)) + return merr.Status(err), nil + } + relatedPrivileges := util.RelatedPrivileges[util.PrivilegeNameForMetastore(req.Grantor.Privilege.Name)] + if len(relatedPrivileges) != 0 { + for _, relatedPrivilege := range relatedPrivileges { + relatedReq := proto.Clone(request).(*milvuspb.OperatePrivilegeRequest) + relatedReq.Entity.Grantor.Privilege.Name = util.PrivilegeNameForAPI(relatedPrivilege) + result, err = node.rootCoord.OperatePrivilege(ctx, relatedReq) + if err != nil { + log.Warn("fail to operate related privilege", zap.String("related_privilege", relatedPrivilege), zap.Error(err)) + return merr.Status(err), nil + } + if !merr.Ok(result) { + log.Warn("fail to operate related privilege", zap.String("related_privilege", relatedPrivilege), zap.Any("result", result)) + return result, nil + } + } + } + if merr.Ok(result) { + SendReplicateMessagePack(ctx, node.replicateMsgStream, req) + } + return result, nil +} + func (node *Proxy) OperatePrivilege(ctx context.Context, req *milvuspb.OperatePrivilegeRequest) (*commonpb.Status, error) { ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-OperatePrivilege") defer sp.End() @@ -6576,8 +6657,11 @@ func (node *Proxy) CreatePrivilegeGroup(ctx context.Context, req *milvuspb.Creat if err := merr.CheckHealthy(node.GetStateCode()); err != nil { return merr.Status(err), nil } - if req.GroupName == "" { - return merr.Status(fmt.Errorf("the group name in the drop privilege group request is nil")), nil + if err := ValidatePrivilegeGroupName(req.GroupName); err != nil { + log.Warn("CreatePrivilegeGroup failed", + zap.Error(err), + ) + return getErrResponse(err, "CreatePrivilegeGroup", "", ""), nil } if req.Base == nil { req.Base = &commonpb.MsgBase{} @@ -6605,8 +6689,11 @@ func (node *Proxy) DropPrivilegeGroup(ctx context.Context, req *milvuspb.DropPri if err := merr.CheckHealthy(node.GetStateCode()); err != nil { return merr.Status(err), nil } - if req.GroupName == "" { - return merr.Status(fmt.Errorf("the group name in the drop privilege group request is nil")), nil + if err := ValidatePrivilegeGroupName(req.GroupName); err != nil { + log.Warn("DropPrivilegeGroup failed", + zap.Error(err), + ) + return getErrResponse(err, "DropPrivilegeGroup", "", ""), nil } if req.Base == nil { req.Base = &commonpb.MsgBase{} @@ -6663,8 +6750,11 @@ func (node *Proxy) OperatePrivilegeGroup(ctx context.Context, req *milvuspb.Oper if err := merr.CheckHealthy(node.GetStateCode()); err != nil { return merr.Status(err), nil } - if req.GroupName == "" { - return merr.Status(fmt.Errorf("the group name in the drop privilege group request is nil")), nil + if err := ValidatePrivilegeGroupName(req.GroupName); err != nil { + log.Warn("OperatePrivilegeGroup failed", + zap.Error(err), + ) + return getErrResponse(err, "OperatePrivilegeGroup", "", ""), nil } for _, priv := range req.GetPrivileges() { if err := ValidatePrivilege(priv.Name); err != nil { diff --git a/internal/proxy/privilege_interceptor.go b/internal/proxy/privilege_interceptor.go index fc78fc3538..9656cf244c 100644 --- a/internal/proxy/privilege_interceptor.go +++ b/internal/proxy/privilege_interceptor.go @@ -162,8 +162,8 @@ func PrivilegeInterceptor(ctx context.Context, req interface{}) (context.Context e := getEnforcer() for _, roleName := range roleNames { - permitFunc := func(resName string) (bool, error) { - object := funcutil.PolicyForResource(dbName, objectType, resName) + permitFunc := func(objectName string) (bool, error) { + object := funcutil.PolicyForResource(dbName, objectType, objectName) isPermit, cached, version := GetPrivilegeCache(roleName, object, objectPrivilege) if cached { return isPermit, nil diff --git a/internal/proxy/privilege_interceptor_test.go b/internal/proxy/privilege_interceptor_test.go index 92621d010e..e8093e0985 100644 --- a/internal/proxy/privilege_interceptor_test.go +++ b/internal/proxy/privilege_interceptor_test.go @@ -563,3 +563,45 @@ func TestPrivilegeGroup(t *testing.T) { assert.NoError(t, err) }) } + +func TestBuiltinPrivilegeGroup(t *testing.T) { + t.Run("ClusterAdmin", 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() + + policies := []string{} + for _, priv := range util.BuiltinPrivilegeGroups["ClusterReadOnly"] { + objectType := util.GetObjectType(priv) + policies = append(policies, funcutil.PolicyForPrivilege("role1", objectType, "*", util.PrivilegeNameForMetastore(priv), "default")) + } + client.listPolicy = func(ctx context.Context, in *internalpb.ListPolicyRequest) (*internalpb.ListPolicyResponse, error) { + return &internalpb.ListPolicyResponse{ + Status: merr.Success(), + PolicyInfos: policies, + UserRoles: []string{ + funcutil.EncodeUserRoleCache("fooo", "role1"), + }, + }, nil + } + InitMetaCache(ctx, client, queryCoord, mgr) + defer CleanPrivilegeCache() + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SelectUserRequest{}) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DescribeResourceGroupRequest{}) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.ListResourceGroupsRequest{}) + assert.NoError(t, err) + + _, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.CreateResourceGroupRequest{}) + assert.Error(t, err) + }) +} diff --git a/internal/proxy/proxy_test.go b/internal/proxy/proxy_test.go index a3e066ba00..b8eed64bdf 100644 --- a/internal/proxy/proxy_test.go +++ b/internal/proxy/proxy_test.go @@ -2709,6 +2709,8 @@ func TestProxy(t *testing.T) { testProxyRole(ctx, t, proxy) testProxyPrivilege(ctx, t, proxy) + testProxyOperatePrivilegeV2(ctx, t, proxy) + assert.False(t, false, true) testProxyRefreshPolicyInfoCache(ctx, t, proxy) // proxy unhealthy @@ -4377,6 +4379,106 @@ func testProxyPrivilege(ctx context.Context, t *testing.T, proxy *Proxy) { wg.Wait() } +func testProxyOperatePrivilegeV2(ctx context.Context, t *testing.T, proxy *Proxy) { + var wg sync.WaitGroup + wg.Add(1) + t.Run("Operate Privilege V2, Select Grant", func(t *testing.T) { + defer wg.Done() + + // GrantPrivilege + req := &milvuspb.OperatePrivilegeV2Request{} + resp, _ := proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.Role = &milvuspb.RoleEntity{} + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.Grantor = &milvuspb.GrantorEntity{} + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.Grantor.Privilege = &milvuspb.PrivilegeEntity{} + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.Grantor.Privilege.Name = util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeAll.String()) + + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.DbName = "" + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + req.CollectionName = "" + resp, _ = proxy.OperatePrivilegeV2(ctx, req) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq := &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeLoad.String())}}, + DbName: "default", + CollectionName: "col1", + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.Equal(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq = &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadOnly.String())}}, + DbName: util.AnyWord, + CollectionName: util.AnyWord, + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.Equal(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq = &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadOnly.String())}}, + DbName: "db1", + CollectionName: util.AnyWord, + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq = &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupCollectionReadOnly.String())}}, + DbName: "db1", + CollectionName: util.AnyWord, + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.Equal(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq = &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupClusterReadOnly.String())}}, + DbName: "db1", + CollectionName: "col1", + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) + + roleReq = &milvuspb.OperatePrivilegeV2Request{ + Role: &milvuspb.RoleEntity{Name: "public"}, + Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: util.MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupDatabaseReadOnly.String())}}, + DbName: "db1", + CollectionName: util.AnyWord, + Type: milvuspb.OperatePrivilegeType_Grant, + } + resp, _ = proxy.OperatePrivilegeV2(ctx, roleReq) + assert.Equal(t, commonpb.ErrorCode_Success, resp.ErrorCode) + }) + + wg.Wait() +} + func testProxyPrivilegeUnhealthy(ctx context.Context, t *testing.T, proxy *Proxy) { testProxyPrivilegeFail(ctx, t, proxy, "unhealthy") } diff --git a/internal/proxy/util.go b/internal/proxy/util.go index 326823fc32..1468a283fb 100644 --- a/internal/proxy/util.go +++ b/internal/proxy/util.go @@ -156,6 +156,32 @@ func validateCollectionNameOrAlias(entity, entityType string) error { return nil } +func ValidatePrivilegeGroupName(groupName string) error { + if groupName == "" { + return merr.WrapErrDatabaseNameInvalid(groupName, "privilege group name couldn't be empty") + } + + if len(groupName) > Params.ProxyCfg.MaxNameLength.GetAsInt() { + return merr.WrapErrDatabaseNameInvalid(groupName, + fmt.Sprintf("the length of a privilege group name must be less than %d characters", Params.ProxyCfg.MaxNameLength.GetAsInt())) + } + + firstChar := groupName[0] + if firstChar != '_' && !isAlpha(firstChar) { + return merr.WrapErrDatabaseNameInvalid(groupName, + "the first character of a privilege group name must be an underscore or letter") + } + + for i := 1; i < len(groupName); i++ { + c := groupName[i] + if c != '_' && !isAlpha(c) && !isNumber(c) { + return merr.WrapErrDatabaseNameInvalid(groupName, + "privilege group name can only contain numbers, letters and underscores") + } + } + return nil +} + func ValidateResourceGroupName(entity string) error { if entity == "" { return errors.New("resource group name couldn't be empty") diff --git a/internal/rootcoord/mock_test.go b/internal/rootcoord/mock_test.go index 2764a6d861..9dc973be81 100644 --- a/internal/rootcoord/mock_test.go +++ b/internal/rootcoord/mock_test.go @@ -99,6 +99,7 @@ type mockMetaTable struct { DescribeDatabaseFunc func(ctx context.Context, dbName string) (*model.Database, error) CreatePrivilegeGroupFunc func(ctx context.Context, groupName string) error DropPrivilegeGroupFunc func(ctx context.Context, groupName string) error + IsCustomPrivilegeGroupFunc func(ctx context.Context, groupName string) (bool, error) ListPrivilegeGroupsFunc func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) OperatePrivilegeGroupFunc func(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error GetPrivilegeGroupRolesFunc func(ctx context.Context, groupName string) ([]*milvuspb.RoleEntity, error) @@ -268,6 +269,10 @@ func (m mockMetaTable) ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.Pri return m.ListPrivilegeGroupsFunc(ctx) } +func (m mockMetaTable) IsCustomPrivilegeGroup(ctx context.Context, groupName string) (bool, error) { + return m.IsCustomPrivilegeGroupFunc(ctx, groupName) +} + func (m mockMetaTable) OperatePrivilegeGroup(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error { return m.OperatePrivilegeGroupFunc(ctx, groupName, privileges, operateType) } @@ -561,6 +566,9 @@ func withInvalidMeta() Opt { meta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) { return nil, errors.New("error mock ListPrivilegeGroups") } + meta.IsCustomPrivilegeGroupFunc = func(ctx context.Context, groupName string) (bool, error) { + return false, errors.New("error mock IsCustomPrivilegeGroup") + } meta.OperatePrivilegeGroupFunc = func(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error { return errors.New("error mock OperatePrivilegeGroup") } diff --git a/internal/rootcoord/root_coord.go b/internal/rootcoord/root_coord.go index 349c5d336b..d46bb318e2 100644 --- a/internal/rootcoord/root_coord.go +++ b/internal/rootcoord/root_coord.go @@ -22,6 +22,7 @@ import ( "math/rand" "os" "strconv" + "strings" "sync" "time" @@ -2554,44 +2555,72 @@ func (c *Core) isValidObject(entity *milvuspb.ObjectEntity) error { return nil } -func (c *Core) isValidGrantor(ctx context.Context, entity *milvuspb.GrantorEntity, object string) error { - if entity == nil { - return errors.New("the grantor entity is nil") - } - if entity.User == nil || entity.User.Name == "" { - return errors.New("the user entity in the grantor entity is nil or empty") - } - if _, err := c.meta.SelectUser(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: entity.User.Name}, false); err != nil { - log.Warn("fail to select the user", zap.String("username", entity.User.Name), zap.Error(err)) - return errors.New("not found the user, maybe the user isn't existed or internal system error") - } - if entity.Privilege == nil { - return errors.New("the privilege entity in the grantor entity is nil") - } - if util.IsAnyWord(entity.Privilege.Name) { +func (c *Core) isValidPrivilege(ctx context.Context, privilegeName string, object string) error { + if util.IsAnyWord(privilegeName) { return nil } + customPrivGroup, err := c.meta.IsCustomPrivilegeGroup(ctx, privilegeName) + if err != nil { + return err + } + if customPrivGroup { + return fmt.Errorf("can not operate the custom privilege group [%s]", privilegeName) + } + if lo.Contains(lo.Keys(util.BuiltinPrivilegeGroups), privilegeName) { + return fmt.Errorf("can not operate the built-in privilege group [%s]", privilegeName) + } // check object privileges for built-in privileges - if util.IsPrivilegeNameDefined(entity.Privilege.Name) { + if util.IsPrivilegeNameDefined(privilegeName) { privileges, ok := util.ObjectPrivileges[object] if !ok { return fmt.Errorf("not found the object type[name: %s], supported the object types: %v", object, lo.Keys(commonpb.ObjectType_value)) } for _, privilege := range privileges { - if privilege == entity.Privilege.Name { + if privilege == privilegeName { return nil } } } - // check if it is a custom privilege group - customPrivGroup, err := c.meta.IsCustomPrivilegeGroup(ctx, entity.Privilege.Name) - if err != nil { - return err - } - if customPrivGroup { + return fmt.Errorf("not found the privilege name[%s] in object[%s]", privilegeName, object) +} + +func (c *Core) isValidPrivilegeV2(ctx context.Context, privilegeName, dbName, collectionName string) error { + if util.IsAnyWord(privilegeName) { + return nil + } + var privilegeLevel string + for group, privileges := range util.BuiltinPrivilegeGroups { + if privilegeName == group || lo.Contains(privileges, privilegeName) { + privilegeLevel = group + break + } + } + if privilegeLevel == "" { + customPrivGroup, err := c.meta.IsCustomPrivilegeGroup(ctx, privilegeName) + if err != nil { + return err + } + if customPrivGroup { + return nil + } + return fmt.Errorf("not found the privilege name[%s] in the custom privilege groups", privilegeName) + } + switch { + case strings.HasPrefix(privilegeLevel, milvuspb.PrivilegeLevel_Cluster.String()): + if !util.IsAnyWord(dbName) || !util.IsAnyWord(collectionName) { + return fmt.Errorf("dbName and collectionName should be * for the cluster level privilege: %s", privilegeName) + } + return nil + case strings.HasPrefix(privilegeLevel, milvuspb.PrivilegeLevel_Database.String()): + if collectionName != "" && collectionName != util.AnyWord { + return fmt.Errorf("collectionName should be empty or * for the database level privilege: %s", privilegeName) + } + return nil + case strings.HasPrefix(privilegeLevel, milvuspb.PrivilegeLevel_Collection.String()): + return nil + default: return nil } - return fmt.Errorf("not found the privilege name[%s] in object[%s]", entity.Privilege.Name, object) } // OperatePrivilege operate the privilege, including grant and revoke @@ -2608,37 +2637,29 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile ctxLog := log.Ctx(ctx).With(zap.String("role", typeutil.RootCoordRole), zap.Any("in", in)) ctxLog.Debug(method) - if err := merr.CheckHealthy(c.GetStateCode()); err != nil { - return merr.Status(err), nil - } - if in.Type != milvuspb.OperatePrivilegeType_Grant && in.Type != milvuspb.OperatePrivilegeType_Revoke { - errMsg := fmt.Sprintf("invalid operate privilege type, current type: %s, valid value: [%s, %s]", in.Type, milvuspb.OperatePrivilegeType_Grant, milvuspb.OperatePrivilegeType_Revoke) - ctxLog.Warn(errMsg) - return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperatePrivilegeFailure), nil - } - if in.Entity == nil { - errMsg := "the grant entity in the request is nil" - ctxLog.Error(errMsg) - return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperatePrivilegeFailure), nil - } - if err := c.isValidObject(in.Entity.Object); err != nil { - ctxLog.Warn("", zap.Error(err)) - return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil - } - if err := c.isValidRole(in.Entity.Role); err != nil { - ctxLog.Warn("", zap.Error(err)) - return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil - } - if err := c.isValidGrantor(ctx, in.Entity.Grantor, in.Entity.Object.Name); err != nil { - ctxLog.Error("", zap.Error(err)) + if err := c.operatePrivilegeCommonCheck(ctx, in); err != nil { return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil } - // set up object name if it is global object type - if in.Entity.Object.Name == commonpb.ObjectType_Global.String() { - in.Entity.ObjectName = util.AnyWord + switch in.Version { + case "v2": + if err := c.isValidPrivilegeV2(ctx, in.Entity.Grantor.Privilege.Name, + in.Entity.DbName, in.Entity.ObjectName); err != nil { + ctxLog.Error("", zap.Error(err)) + return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil + } + default: + if err := c.isValidPrivilege(ctx, in.Entity.Grantor.Privilege.Name, in.Entity.Object.Name); err != nil { + ctxLog.Error("", zap.Error(err)) + return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil + } + // set up object name if it is global object type and not built in privilege group + if in.Entity.Object.Name == commonpb.ObjectType_Global.String() && !lo.Contains(lo.Keys(util.BuiltinPrivilegeGroups), in.Entity.Grantor.Privilege.Name) { + in.Entity.ObjectName = util.AnyWord + } } + // set up privilege name for metastore privName := in.Entity.Grantor.Privilege.Name redoTask := newBaseRedoTask(c.stepExecutor) @@ -2709,6 +2730,41 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile return merr.Success(), nil } +func (c *Core) operatePrivilegeCommonCheck(ctx context.Context, in *milvuspb.OperatePrivilegeRequest) error { + if err := merr.CheckHealthy(c.GetStateCode()); err != nil { + return err + } + if in.Type != milvuspb.OperatePrivilegeType_Grant && in.Type != milvuspb.OperatePrivilegeType_Revoke { + errMsg := fmt.Sprintf("invalid operate privilege type, current type: %s, valid value: [%s, %s]", in.Type, milvuspb.OperatePrivilegeType_Grant, milvuspb.OperatePrivilegeType_Revoke) + return errors.New(errMsg) + } + if in.Entity == nil { + errMsg := "the grant entity in the request is nil" + return errors.New(errMsg) + } + if err := c.isValidObject(in.Entity.Object); err != nil { + return errors.New("the object entity in the request is nil or invalid") + } + if err := c.isValidRole(in.Entity.Role); err != nil { + return err + } + entity := in.Entity.Grantor + if entity == nil { + return errors.New("the grantor entity is nil") + } + if entity.User == nil || entity.User.Name == "" { + return errors.New("the user entity in the grantor entity is nil or empty") + } + if _, err := c.meta.SelectUser(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: entity.User.Name}, false); err != nil { + log.Warn("fail to select the user", zap.String("username", entity.User.Name), zap.Error(err)) + return errors.New("not found the user, maybe the user isn't existed or internal system error") + } + if entity.Privilege == nil { + return errors.New("the privilege entity in the grantor entity is nil") + } + return nil +} + 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) { @@ -3105,6 +3161,18 @@ func (c *Core) ListPrivilegeGroups(ctx context.Context, in *milvuspb.ListPrivile ctxLog.Debug(method + " success") metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc() metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) + + // append built in privilege groups + for groupName, privileges := range util.BuiltinPrivilegeGroups { + privGroups = append(privGroups, &milvuspb.PrivilegeGroupInfo{ + GroupName: groupName, + Privileges: lo.Map(privileges, func(p string, _ int) *milvuspb.PrivilegeEntity { + return &milvuspb.PrivilegeEntity{ + Name: p, + } + }), + }) + } return &milvuspb.ListPrivilegeGroupsResponse{ Status: merr.Success(), PrivilegeGroups: privGroups, @@ -3151,6 +3219,14 @@ func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePr newGroups[k] = lo.UniqBy(newPrivs, func(p *milvuspb.PrivilegeEntity) string { 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{}{} + }) + if len(objectTypes) > 1 { + return nil, errors.New("privileges are not the same object type") + } case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup: newPrivs, _ := lo.Difference(v, in.Privileges) newGroups[k] = newPrivs @@ -3162,11 +3238,11 @@ func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePr rolesToRevoke := []*milvuspb.GrantEntity{} rolesToGrant := []*milvuspb.GrantEntity{} compareGrants := func(a, b *milvuspb.GrantEntity) bool { - return a.Role.GetName() == b.Role.GetName() && - a.Object.GetName() == b.Object.GetName() && + return a.Role.Name == b.Role.Name && + a.Object.Name == b.Object.Name && a.ObjectName == b.ObjectName && - a.Grantor.GetUser().GetName() == b.Grantor.GetUser().GetName() && - a.Grantor.GetPrivilege().GetName() == b.Grantor.GetPrivilege().GetName() && + a.Grantor.User.Name == b.Grantor.User.Name && + a.Grantor.Privilege.Name == b.Grantor.Privilege.Name && a.DbName == b.DbName } for _, role := range roles { @@ -3250,43 +3326,43 @@ func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePr func (c *Core) expandPrivilegeGroups(ctx context.Context, grants []*milvuspb.GrantEntity, groups map[string][]*milvuspb.PrivilegeEntity) ([]*milvuspb.GrantEntity, error) { newGrants := []*milvuspb.GrantEntity{} + createGrantEntity := func(grant *milvuspb.GrantEntity, privilegeName string) (*milvuspb.GrantEntity, error) { + metaName, err := c.getMetastorePrivilegeName(ctx, privilegeName) + if err != nil { + return nil, err + } + if objectType := util.GetObjectType(privilegeName); objectType != "" { + grant.Object.Name = objectType + } + return &milvuspb.GrantEntity{ + Role: grant.Role, + Object: grant.Object, + ObjectName: grant.ObjectName, + Grantor: &milvuspb.GrantorEntity{ + User: grant.Grantor.User, + Privilege: &milvuspb.PrivilegeEntity{ + Name: metaName, + }, + }, + DbName: grant.DbName, + }, nil + } + for _, grant := range grants { privName := grant.Grantor.Privilege.Name if privGroup, exists := groups[privName]; !exists { - metaName, err := c.getMetastorePrivilegeName(ctx, privName) + newGrant, err := createGrantEntity(grant, privName) if err != nil { return nil, err } - newGrants = append(newGrants, &milvuspb.GrantEntity{ - Role: grant.Role, - Object: grant.Object, - ObjectName: grant.ObjectName, - Grantor: &milvuspb.GrantorEntity{ - User: grant.Grantor.User, - Privilege: &milvuspb.PrivilegeEntity{ - Name: metaName, - }, - }, - DbName: grant.DbName, - }) + newGrants = append(newGrants, newGrant) } else { for _, priv := range privGroup { - metaName, err := c.getMetastorePrivilegeName(ctx, priv.Name) + newGrant, err := createGrantEntity(grant, priv.Name) if err != nil { return nil, err } - newGrants = append(newGrants, &milvuspb.GrantEntity{ - Role: grant.Role, - Object: grant.Object, - ObjectName: grant.ObjectName, - Grantor: &milvuspb.GrantorEntity{ - User: grant.Grantor.User, - Privilege: &milvuspb.PrivilegeEntity{ - Name: metaName, - }, - }, - DbName: grant.DbName, - }) + newGrants = append(newGrants, newGrant) } } } diff --git a/internal/rootcoord/root_coord_test.go b/internal/rootcoord/root_coord_test.go index f2fbf1b286..c9a803f642 100644 --- a/internal/rootcoord/root_coord_test.go +++ b/internal/rootcoord/root_coord_test.go @@ -1764,7 +1764,9 @@ func TestRootCoord_RBACError(t *testing.T) { assert.NoError(t, err) assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode) } - + mockMeta.IsCustomPrivilegeGroupFunc = func(ctx context.Context, groupName string) (bool, error) { + return false, nil + } mockMeta.SelectUserFunc = func(ctx context.Context, tenant string, entity *milvuspb.UserEntity, includeRoleInfo bool) ([]*milvuspb.UserResult, error) { return nil, nil } diff --git a/pkg/go.mod b/pkg/go.mod index 60b97e39a1..c97f640927 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -14,7 +14,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/json-iterator/go v1.1.12 github.com/klauspost/compress v1.17.7 - github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c + github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69 github.com/nats-io/nats-server/v2 v2.10.12 github.com/nats-io/nats.go v1.34.1 github.com/panjf2000/ants/v2 v2.7.2 diff --git a/pkg/go.sum b/pkg/go.sum index c1f60d82a5..e5876f5b27 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -488,8 +488,8 @@ github.com/milvus-io/cgosymbolizer v0.0.0-20240722103217-b7dee0e50119 h1:9VXijWu github.com/milvus-io/cgosymbolizer v0.0.0-20240722103217-b7dee0e50119/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8= github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4= -github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c h1:Ay5w6sTE1QxCydCqqW5N44EcJrMqaqbL5zcp2vclkOw= -github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241114133823-d3506c6f465c/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69 h1:Qt0Bv2Fum3EX3OlkuQYHJINBzeU4oEuHy2lXSfB/gZw= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241120015424-93892e628c69/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs= github.com/milvus-io/pulsar-client-go v0.12.1 h1:O2JZp1tsYiO7C0MQ4hrUY/aJXnn2Gry6hpm7UodghmE= github.com/milvus-io/pulsar-client-go v0.12.1/go.mod h1:dkutuH4oS2pXiGm+Ti7fQZ4MRjrMPZ8IJeEGAWMeckk= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= diff --git a/pkg/util/constant.go b/pkg/util/constant.go index 920a50171f..a1323f7ab1 100644 --- a/pkg/util/constant.go +++ b/pkg/util/constant.go @@ -19,6 +19,8 @@ package util import ( "strings" + "github.com/samber/lo" + "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" "github.com/milvus-io/milvus/pkg/common" "github.com/milvus-io/milvus/pkg/util/typeutil" @@ -466,3 +468,12 @@ func IsBuiltinRole(roleName string) bool { } return false } + +func GetObjectType(privName string) string { + for objectType, privs := range ObjectPrivileges { + if lo.Contains(privs, privName) { + return objectType + } + } + return "" +} diff --git a/tests/integration/rbac/privilege_group_test.go b/tests/integration/rbac/privilege_group_test.go index 2a3f814df9..1f1b13d41b 100644 --- a/tests/integration/rbac/privilege_group_test.go +++ b/tests/integration/rbac/privilege_group_test.go @@ -18,8 +18,10 @@ package rbac import ( "context" "fmt" + "strings" "testing" + "github.com/samber/lo" "github.com/stretchr/testify/suite" "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" @@ -46,10 +48,10 @@ func (s *PrivilegeGroupTestSuite) TestBuiltinPrivilegeGroup() { ctx := GetContext(context.Background(), "root:123456") // Test empty RBAC content - resp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{}) + backupResp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{}) s.NoError(err) - s.True(merr.Ok(resp.GetStatus())) - s.Equal("", resp.GetRBACMeta().String()) + s.True(merr.Ok(backupResp.GetStatus())) + s.Equal("", backupResp.GetRBACMeta().String()) // Generate some RBAC content roleName := "test_role" @@ -59,34 +61,130 @@ func (s *PrivilegeGroupTestSuite) TestBuiltinPrivilegeGroup() { s.NoError(err) s.True(merr.Ok(createRoleResp)) - s.operatePrivilege(ctx, roleName, "ReadOnly", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "ReadWrite", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "Admin", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) + resp, _ := s.operatePrivilege(ctx, roleName, "ReadOnly", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilege(ctx, roleName, "ReadWrite", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilege(ctx, roleName, "Admin", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) - s.operatePrivilege(ctx, roleName, "ClusterReadOnly", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "ClusterReadWrite", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "ClusterAdmin", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "DatabaseReadOnly", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "DatabaseReadWrite", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "DatabaseAdmin", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "CollectionReadOnly", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "CollectionReadWrite", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) - s.operatePrivilege(ctx, roleName, "CollectionAdmin", commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) + for _, builtinGroup := range lo.Keys(util.BuiltinPrivilegeGroups) { + fmt.Println("!!! builtinGroup: ", builtinGroup) + resp, _ = s.operatePrivilege(ctx, roleName, builtinGroup, commonpb.ObjectType_Global.String(), milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + } - s.validateGrants(ctx, roleName, commonpb.ObjectType_Global.String(), 10) + s.validateGrants(ctx, roleName, commonpb.ObjectType_Global.String(), 1) s.validateGrants(ctx, roleName, commonpb.ObjectType_Collection.String(), 2) } -/* -create group1: query, search -grant insert to role -> role: insert -grant group1 to role -> role: insert, group1(query, search) -create group2: query, delete -grant group2 to role -> role: insert, group1(query, search), group2(query, delete) -add query, load to group1 -> group1: query, search, load -> role: insert, group1(query, search, load), group2(query, delete) -remove query from group1 -> group1: search, load -> role: insert, group1(search, load), group2(query, delete), role still have query privilege because of group2 granted. -*/ -func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { +func (s *PrivilegeGroupTestSuite) TestInvalidPrivilegeGroup() { + ctx := GetContext(context.Background(), "root:123456") + + createResp, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ + GroupName: "", + }) + s.NoError(err) + s.False(merr.Ok(createResp)) + + // create group %$ will fail + createResp, _ = s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ + GroupName: "%$", + }) + s.False(merr.Ok(createResp)) + + createResp, _ = s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ + GroupName: "a&", + }) + s.False(merr.Ok(createResp)) + + createResp, _ = s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ + GroupName: strings.Repeat("a", 300), + }) + s.False(merr.Ok(createResp)) + + // drop group %$ will fail + dropResp, _ := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ + GroupName: "%$", + }) + s.False(merr.Ok(dropResp)) + + dropResp, err = s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ + GroupName: "group1", + }) + s.NoError(err) + s.True(merr.Ok(dropResp)) + + dropResp, err = s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ + GroupName: "", + }) + s.NoError(err) + s.False(merr.Ok(dropResp)) + + operateResp, err := s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ + GroupName: "", + }) + s.NoError(err) + s.False(merr.Ok(operateResp)) + + operateResp, err = s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ + GroupName: "group1", + Privileges: []*milvuspb.PrivilegeEntity{{Name: "123"}}, + }) + s.NoError(err) + s.False(merr.Ok(operateResp)) +} + +func (s *PrivilegeGroupTestSuite) TestGrantV2BuiltinPrivilegeGroup() { + ctx := GetContext(context.Background(), "root:123456") + + roleName := "test_role" + createRoleResp, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{ + Entity: &milvuspb.RoleEntity{Name: roleName}, + }) + s.NoError(err) + s.True(merr.Ok(createRoleResp)) + + resp, _ := s.operatePrivilegeV2(ctx, roleName, "ClusterReadOnly", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "ClusterReadWrite", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "ClusterAdmin", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseReadOnly", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseReadWrite", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseAdmin", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionReadOnly", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionReadWrite", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionAdmin", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + + resp, _ = s.operatePrivilegeV2(ctx, roleName, "ClusterAdmin", "db1", util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "ClusterAdmin", "db1", "col1", milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "ClusterAdmin", util.AnyWord, "col1", milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseAdmin", "db1", util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseAdmin", "db1", "col1", milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "DatabaseAdmin", util.AnyWord, "col1", milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionAdmin", "db1", util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionAdmin", "db1", "col1", milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, roleName, "CollectionAdmin", util.AnyWord, "col1", milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) +} + +func (s *PrivilegeGroupTestSuite) TestGrantV2CustomPrivilegeGroup() { ctx := GetContext(context.Background(), "root:123456") // Helper function to operate on privilege groups @@ -133,12 +231,14 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { }) s.NoError(err) s.True(merr.Ok(createRoleResp)) - s.operatePrivilege(ctx, role, "Insert", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) - s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 1) + resp, _ := s.operatePrivilegeV2(ctx, role, "Insert", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), 1) // grant group1 to role -> role: insert, group1(query, search) - s.operatePrivilege(ctx, role, "group1", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) - s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 2) + resp, _ = s.operatePrivilegeV2(ctx, role, "group1", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), 2) // create group2: query, delete createResp2, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ @@ -154,8 +254,9 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { validatePrivilegeGroup("group2", 2) // grant group2 to role -> role: insert, group1(query, search), group2(query, delete) - s.operatePrivilege(ctx, role, "group2", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Grant) - s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 3) + resp, _ = s.operatePrivilegeV2(ctx, role, "group2", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), 3) // add query, load to group1 -> group1: query, search, load -> role: insert, group1(query, search, load), group2(query, delete) operatePrivilegeGroup("group1", milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup, []*milvuspb.PrivilegeEntity{ @@ -163,14 +264,22 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { {Name: "Load"}, }) validatePrivilegeGroup("group1", 3) - s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 3) + s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), 3) + + // add different object type privileges to group1 is not allowed + resp, _ = s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ + GroupName: "group1", + Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup, + Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}}, + }) + s.Error(merr.Error(resp)) // remove query from group1 -> group1: search, load -> role: insert, group1(search, load), group2(query, delete), role still have query privilege because of group2 granted. operatePrivilegeGroup("group1", milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup, []*milvuspb.PrivilegeEntity{ {Name: "Query"}, }) validatePrivilegeGroup("group1", 2) - s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 3) + s.validateGrants(ctx, role, commonpb.ObjectType_Global.String(), 3) // Drop the group during any role usage will cause error dropResp, _ := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ @@ -179,9 +288,12 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { s.Error(merr.Error(dropResp)) // Revoke privilege group and privileges - s.operatePrivilege(ctx, role, "group1", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Revoke) - s.operatePrivilege(ctx, role, "group2", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Revoke) - s.operatePrivilege(ctx, role, "Insert", commonpb.ObjectType_Collection.String(), milvuspb.OperatePrivilegeType_Revoke) + resp, _ = s.operatePrivilegeV2(ctx, role, "group1", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, role, "group2", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + s.True(merr.Ok(resp)) + resp, _ = s.operatePrivilegeV2(ctx, role, "Insert", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + s.True(merr.Ok(resp)) // Drop the privilege group after revoking the privilege will succeed dropResp, err = s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ @@ -197,9 +309,22 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { s.True(merr.Ok(dropResp)) // Validate the group was dropped - resp, err := s.Cluster.Proxy.ListPrivilegeGroups(ctx, &milvuspb.ListPrivilegeGroupsRequest{}) + listResp, err := s.Cluster.Proxy.ListPrivilegeGroups(ctx, &milvuspb.ListPrivilegeGroupsRequest{}) s.NoError(err) - s.Equal(0, len(resp.PrivilegeGroups)) + s.Equal(len(util.BuiltinPrivilegeGroups), len(listResp.PrivilegeGroups)) + + // validate edge cases + resp, _ = s.operatePrivilegeV2(ctx, role, util.AnyWord, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.True(merr.Ok(resp)) + + resp, _ = s.operatePrivilegeV2(ctx, role, util.AnyWord, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + s.True(merr.Ok(resp)) + + resp, _ = s.operatePrivilegeV2(ctx, role, "group3", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke) + s.False(merr.Ok(resp)) + + resp, _ = s.operatePrivilegeV2(ctx, role, "%$", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant) + s.False(merr.Ok(resp)) // Drop the role dropRoleResp, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{ @@ -209,42 +334,7 @@ func (s *PrivilegeGroupTestSuite) TestCustomPrivilegeGroup() { s.True(merr.Ok(dropRoleResp)) } -func (s *PrivilegeGroupTestSuite) TestInvalidPrivilegeGroup() { - ctx := GetContext(context.Background(), "root:123456") - - createResp, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{ - GroupName: "", - }) - s.NoError(err) - s.False(merr.Ok(createResp)) - - dropResp, err := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ - GroupName: "group1", - }) - s.NoError(err) - s.True(merr.Ok(dropResp)) - - dropResp, err = s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{ - GroupName: "", - }) - s.NoError(err) - s.False(merr.Ok(dropResp)) - - operateResp, err := s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ - GroupName: "", - }) - s.NoError(err) - s.False(merr.Ok(operateResp)) - - operateResp, err = s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{ - GroupName: "group1", - Privileges: []*milvuspb.PrivilegeEntity{{Name: "123"}}, - }) - s.NoError(err) - s.False(merr.Ok(operateResp)) -} - -func (s *PrivilegeGroupTestSuite) operatePrivilege(ctx context.Context, role, privilege, objectType string, operateType milvuspb.OperatePrivilegeType) { +func (s *PrivilegeGroupTestSuite) operatePrivilege(ctx context.Context, role, privilege, objectType string, operateType milvuspb.OperatePrivilegeType) (*commonpb.Status, error) { resp, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{ Type: operateType, Entity: &milvuspb.GrantEntity{ @@ -258,8 +348,25 @@ func (s *PrivilegeGroupTestSuite) operatePrivilege(ctx context.Context, role, pr }, }, }) - s.NoError(err) - s.True(merr.Ok(resp)) + return resp, err +} + +func (s *PrivilegeGroupTestSuite) operatePrivilegeV2(ctx context.Context, role, privilege, dbName, collectionName string, operateType milvuspb.OperatePrivilegeType) (*commonpb.Status, error) { + resp, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{ + Type: operateType, + Entity: &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: role}, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: collectionName, + DbName: dbName, + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: privilege}, + }, + }, + Version: "v2", + }) + return resp, err } func (s *PrivilegeGroupTestSuite) validateGrants(ctx context.Context, roleName, objectType string, expectedCount int) { @@ -271,7 +378,6 @@ func (s *PrivilegeGroupTestSuite) validateGrants(ctx context.Context, roleName, DbName: util.AnyWord, }, }) - fmt.Println("!!!validateGrants: ", resp) s.NoError(err) s.True(merr.Ok(resp.GetStatus())) s.Len(resp.GetEntities(), expectedCount) diff --git a/tests/integration/rbac/rbac_backup_test.go b/tests/integration/rbac/rbac_backup_test.go index 5bb1e2adb7..49e61ee4dc 100644 --- a/tests/integration/rbac/rbac_backup_test.go +++ b/tests/integration/rbac/rbac_backup_test.go @@ -78,11 +78,15 @@ func (s *RBACBackupTestSuite) TestBackup() { Type: operateType, Entity: &milvuspb.GrantEntity{ Role: &milvuspb.RoleEntity{Name: role}, - Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()}, - ObjectName: objectName, + Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()}, + ObjectName: collectionName, DbName: dbName, - Grantor: &milvuspb.GrantorEntity{User: &milvuspb.UserEntity{Name: util.UserRoot}, Privilege: &milvuspb.PrivilegeEntity{Name: privilege}}, + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: privilege}, + }, }, + Version: "v2", }) s.NoError(err) s.True(merr.Ok(resp))