diff --git a/go.mod b/go.mod index f323a95eb2b5c..a6cc1720ab1b7 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.16.7 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d - github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231114080011-9a495865219e + github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f github.com/milvus-io/milvus/pkg v0.0.1 github.com/minio/minio-go/v7 v7.0.61 github.com/prometheus/client_golang v1.14.0 diff --git a/go.sum b/go.sum index 14aacb81635f8..5c3cc3d9b8c06 100644 --- a/go.sum +++ b/go.sum @@ -583,6 +583,8 @@ github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZz 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.20231114080011-9a495865219e h1:IH1WAXwEF8vbwahPdupi4zzRNWViT4B7fZzIjtRLpG4= github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231114080011-9a495865219e/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f h1:0cAMN9OsgBxlEUY8i1e1ocrBZ/cpu/Kdguz4JWz9fUc= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= github.com/milvus-io/milvus-storage/go v0.0.0-20231109072809-1cd7b0866092 h1:UYJ7JB+QlMOoFHNdd8mUa3/lV63t9dnBX7ILXmEEWPY= github.com/milvus-io/milvus-storage/go v0.0.0-20231109072809-1cd7b0866092/go.mod h1:GPETMcTZq1gLY1WA6Na5kiNAKnq8SEMMiVKUZrM3sho= github.com/milvus-io/pulsar-client-go v0.6.10 h1:eqpJjU+/QX0iIhEo3nhOqMNXL+TyInAs1IAHZCrCM/A= diff --git a/internal/distributed/proxy/service.go b/internal/distributed/proxy/service.go index 75304b72edf9f..ce94260eba397 100644 --- a/internal/distributed/proxy/service.go +++ b/internal/distributed/proxy/service.go @@ -821,6 +821,11 @@ func (s *Server) CreateIndex(ctx context.Context, request *milvuspb.CreateIndexR return s.proxy.CreateIndex(ctx, request) } +func (s *Server) AlterIndex(ctx context.Context, request *milvuspb.AlterIndexRequest) (*commonpb.Status, error) { + // Todo + return nil, nil +} + // DropIndex notifies Proxy to drop index func (s *Server) DropIndex(ctx context.Context, request *milvuspb.DropIndexRequest) (*commonpb.Status, error) { return s.proxy.DropIndex(ctx, request) @@ -865,6 +870,11 @@ func (s *Server) Search(ctx context.Context, request *milvuspb.SearchRequest) (* return s.proxy.Search(ctx, request) } +func (s *Server) SearchV2(ctx context.Context, request *milvuspb.SearchRequestV2) (*milvuspb.SearchResults, error) { + // Todo + return nil, nil +} + func (s *Server) Flush(ctx context.Context, request *milvuspb.FlushRequest) (*milvuspb.FlushResponse, error) { return s.proxy.Flush(ctx, request) } diff --git a/internal/mocks/mock_proxy.go b/internal/mocks/mock_proxy.go index 22715c2f1c4c7..ce1e9e1353f36 100644 --- a/internal/mocks/mock_proxy.go +++ b/internal/mocks/mock_proxy.go @@ -639,6 +639,11 @@ func (_c *MockProxy_CreateIndex_Call) RunAndReturn(run func(context.Context, *mi return _c } +func (_m *MockProxy) AlterIndex(_a0 context.Context, _a1 *milvuspb.AlterIndexRequest) (*commonpb.Status, error) { + // Todo + return nil, nil +} + // CreatePartition provides a mock function with given fields: _a0, _a1 func (_m *MockProxy) CreatePartition(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) { ret := _m.Called(_a0, _a1) @@ -4555,6 +4560,11 @@ func (_c *MockProxy_Search_Call) RunAndReturn(run func(context.Context, *milvusp return _c } +func (_m *MockProxy) SearchV2(_a0 context.Context, _a1 *milvuspb.SearchRequestV2) (*milvuspb.SearchResults, error) { + // Todo + return nil, nil +} + // SelectGrant provides a mock function with given fields: _a0, _a1 func (_m *MockProxy) SelectGrant(_a0 context.Context, _a1 *milvuspb.SelectGrantRequest) (*milvuspb.SelectGrantResponse, error) { ret := _m.Called(_a0, _a1) diff --git a/internal/proxy/impl.go b/internal/proxy/impl.go index 1dff508361b80..907441bbabb3c 100644 --- a/internal/proxy/impl.go +++ b/internal/proxy/impl.go @@ -1814,6 +1814,11 @@ func (node *Proxy) CreateIndex(ctx context.Context, request *milvuspb.CreateInde return cit.result, nil } +func (node *Proxy) AlterIndex(ctx context.Context, request *milvuspb.AlterIndexRequest) (*commonpb.Status, error) { + // Todo + return nil, nil +} + // DescribeIndex get the meta information of index, such as index state, index id and etc. func (node *Proxy) DescribeIndex(ctx context.Context, request *milvuspb.DescribeIndexRequest) (*milvuspb.DescribeIndexResponse, error) { if err := merr.CheckHealthy(node.GetStateCode()); err != nil { @@ -2649,6 +2654,11 @@ func (node *Proxy) Search(ctx context.Context, request *milvuspb.SearchRequest) return qt.result, nil } +func (node *Proxy) SearchV2(ctx context.Context, request *milvuspb.SearchRequestV2) (*milvuspb.SearchResults, error) { + // Todo + return nil, nil +} + func (node *Proxy) getVectorPlaceholderGroupForSearchByPks(ctx context.Context, request *milvuspb.SearchRequest) ([]byte, error) { placeholderGroup := &commonpb.PlaceholderGroup{} err := proto.Unmarshal(request.PlaceholderGroup, placeholderGroup) @@ -4223,7 +4233,7 @@ func (node *Proxy) DropRole(ctx context.Context, req *milvuspb.DropRoleRequest) return merr.Status(err), nil } if IsDefaultRole(req.RoleName) { - err := merr.WrapErrPrivilegeNotPermitted("the role[%s] is a default role, which can't be droped", req.GetRoleName()) + err := merr.WrapErrPrivilegeNotPermitted("the role[%s] is a default role, which can't be dropped", req.GetRoleName()) return merr.Status(err), nil } result, err := node.rootCoord.DropRole(ctx, req) diff --git a/internal/proxy/privilege_interceptor.go b/internal/proxy/privilege_interceptor.go index acd386ded2fee..9eb3e8d77f4f9 100644 --- a/internal/proxy/privilege_interceptor.go +++ b/internal/proxy/privilege_interceptor.go @@ -168,7 +168,7 @@ func PrivilegeInterceptor(ctx context.Context, req interface{}) (context.Context } log.Info("permission deny", zap.Strings("roles", roleNames)) - return ctx, status.Error(codes.PermissionDenied, fmt.Sprintf("%s: permission deny", objectPrivilege)) + return ctx, status.Error(codes.PermissionDenied, fmt.Sprintf("%s: permission deny to %s", objectPrivilege, username)) } // isCurUserObject Determine whether it is an Object of type User that operates on its own user information, diff --git a/internal/rootcoord/root_coord.go b/internal/rootcoord/root_coord.go index d39c78ebf6f8a..11a1de267f1fa 100644 --- a/internal/rootcoord/root_coord.go +++ b/internal/rootcoord/root_coord.go @@ -613,6 +613,43 @@ func (c *Core) initRbac() error { return errors.Wrap(err, "failed to grant collection privilege") } } + if Params.RoleCfg.Enabled.GetAsBool() { + return c.initBuiltinRoles() + } + return nil +} + +func (c *Core) initBuiltinRoles() error { + rolePrivilegesMap := Params.RoleCfg.Roles.GetAsRoleDetails() + for role, privilegesJSON := range rolePrivilegesMap { + err := c.meta.CreateRole(util.DefaultTenant, &milvuspb.RoleEntity{Name: role}) + if err != nil && !common.IsIgnorableError(err) { + log.Error("create a builtin role fail", zap.Any("error", err), zap.String("roleName", role)) + return errors.Wrapf(err, "failed to create a builtin role: %s", role) + } + for _, privilege := range privilegesJSON[util.RoleConfigPrivileges] { + privilegeName := privilege[util.RoleConfigPrivilege] + if !util.IsAnyWord(privilege[util.RoleConfigPrivilege]) { + privilegeName = util.PrivilegeNameForMetastore(privilege[util.RoleConfigPrivilege]) + } + err := c.meta.OperatePrivilege(util.DefaultTenant, &milvuspb.GrantEntity{ + Role: &milvuspb.RoleEntity{Name: role}, + Object: &milvuspb.ObjectEntity{Name: privilege[util.RoleConfigObjectType]}, + ObjectName: privilege[util.RoleConfigObjectName], + DbName: privilege[util.RoleConfigDBName], + Grantor: &milvuspb.GrantorEntity{ + User: &milvuspb.UserEntity{Name: util.UserRoot}, + Privilege: &milvuspb.PrivilegeEntity{Name: privilegeName}, + }, + }, milvuspb.OperatePrivilegeType_Grant) + if err != nil && !common.IsIgnorableError(err) { + log.Error("grant privilege to builtin role fail", zap.Any("error", err), zap.String("roleName", role), zap.Any("privilege", privilege)) + return errors.Wrapf(err, "failed to grant privilege: <%s, %s, %s> of db: %s to role: %s", privilege[util.RoleConfigObjectType], privilege[util.RoleConfigObjectName], privilege[util.RoleConfigPrivilege], privilege[util.RoleConfigDBName], role) + } + } + util.BuiltinRoles = append(util.BuiltinRoles, role) + log.Info("init a builtin role successfully", zap.String("roleName", role)) + } return nil } @@ -2268,6 +2305,10 @@ func (c *Core) DropRole(ctx context.Context, in *milvuspb.DropRoleRequest) (*com if err := merr.CheckHealthy(c.GetStateCode()); err != nil { return merr.Status(err), nil } + for util.IsBuiltinRole(in.GetRoleName()) { + err := merr.WrapErrPrivilegeNotPermitted("the role[%s] is a builtin role, which can't be dropped", in.GetRoleName()) + return merr.Status(err), nil + } if _, err := c.meta.SelectRole(util.DefaultTenant, &milvuspb.RoleEntity{Name: in.RoleName}, false); err != nil { errMsg := "not found the role, maybe the role isn't existed or internal system error" ctxLog.Warn(errMsg, zap.Error(err)) diff --git a/internal/rootcoord/root_coord_test.go b/internal/rootcoord/root_coord_test.go index 29cd64531863b..08a1d998c1ee0 100644 --- a/internal/rootcoord/root_coord_test.go +++ b/internal/rootcoord/root_coord_test.go @@ -49,6 +49,7 @@ import ( "github.com/milvus-io/milvus/internal/util/importutil" "github.com/milvus-io/milvus/internal/util/sessionutil" "github.com/milvus-io/milvus/pkg/common" + "github.com/milvus-io/milvus/pkg/util" "github.com/milvus-io/milvus/pkg/util/etcd" "github.com/milvus-io/milvus/pkg/util/funcutil" "github.com/milvus-io/milvus/pkg/util/merr" @@ -2087,6 +2088,51 @@ func TestRootCoord_RBACError(t *testing.T) { }) } +func TestRootCoord_BuiltinRoles(t *testing.T) { + roleDbAdmin := "db_admin" + paramtable.Init() + paramtable.Get().Save(paramtable.Get().RoleCfg.Enabled.Key, "true") + paramtable.Get().Save(paramtable.Get().RoleCfg.Roles.Key, `{"`+roleDbAdmin+`": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}}`) + t.Run("init builtin roles success", func(t *testing.T) { + c := newTestCore(withHealthyCode(), withInvalidMeta()) + mockMeta := c.meta.(*mockMetaTable) + mockMeta.CreateRoleFunc = func(tenant string, entity *milvuspb.RoleEntity) error { + return nil + } + mockMeta.OperatePrivilegeFunc = func(tenant string, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error { + return nil + } + err := c.initBuiltinRoles() + assert.Equal(t, nil, err) + assert.True(t, util.IsBuiltinRole(roleDbAdmin)) + assert.False(t, util.IsBuiltinRole(util.RoleAdmin)) + resp, err := c.DropRole(context.Background(), &milvuspb.DropRoleRequest{RoleName: roleDbAdmin}) + assert.Equal(t, nil, err) + assert.Equal(t, int32(1401), resp.Code) // merr.ErrPrivilegeNotPermitted + }) + t.Run("init builtin roles fail to create role", func(t *testing.T) { + c := newTestCore(withHealthyCode(), withInvalidMeta()) + mockMeta := c.meta.(*mockMetaTable) + mockMeta.CreateRoleFunc = func(tenant string, entity *milvuspb.RoleEntity) error { + return merr.ErrPrivilegeNotPermitted + } + err := c.initBuiltinRoles() + assert.Error(t, err) + }) + t.Run("init builtin roles fail to operate privileg", func(t *testing.T) { + c := newTestCore(withHealthyCode(), withInvalidMeta()) + mockMeta := c.meta.(*mockMetaTable) + mockMeta.CreateRoleFunc = func(tenant string, entity *milvuspb.RoleEntity) error { + return nil + } + mockMeta.OperatePrivilegeFunc = func(tenant string, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error { + return merr.ErrPrivilegeNotPermitted + } + err := c.initBuiltinRoles() + assert.Error(t, err) + }) +} + func TestCore_Stop(t *testing.T) { t.Run("abnormal stop before component is ready", func(t *testing.T) { c := &Core{} diff --git a/pkg/go.mod b/pkg/go.mod index f2f66b93bba0e..1a092c7a46dbc 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -13,7 +13,7 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/klauspost/compress v1.16.5 github.com/lingdor/stackerror v0.0.0-20191119040541-976d8885ed76 - github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231114080011-9a495865219e + github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f github.com/nats-io/nats-server/v2 v2.9.17 github.com/nats-io/nats.go v1.24.0 github.com/panjf2000/ants/v2 v2.7.2 diff --git a/pkg/go.sum b/pkg/go.sum index ae5a3c8e7df0a..5c20be4bdfb91 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -481,6 +481,8 @@ github.com/milvus-io/milvus-proto/go-api/v2 v2.3.2-0.20231008032233-5d64d443769d github.com/milvus-io/milvus-proto/go-api/v2 v2.3.2-0.20231008032233-5d64d443769d/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231114080011-9a495865219e h1:IH1WAXwEF8vbwahPdupi4zzRNWViT4B7fZzIjtRLpG4= github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231114080011-9a495865219e/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f h1:0cAMN9OsgBxlEUY8i1e1ocrBZ/cpu/Kdguz4JWz9fUc= +github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20231208092431-02cbad30332f/go.mod h1:1OIl0v5PQeNxIJhCvY+K55CBUOYDZevw9g9380u1Wek= github.com/milvus-io/pulsar-client-go v0.6.10 h1:eqpJjU+/QX0iIhEo3nhOqMNXL+TyInAs1IAHZCrCM/A= github.com/milvus-io/pulsar-client-go v0.6.10/go.mod h1:lQqCkgwDF8YFYjKA+zOheTk1tev2B+bKj5j7+nm8M1w= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= diff --git a/pkg/util/constant.go b/pkg/util/constant.go index ecc51c7448a37..1c6e5bf3ffa6d 100644 --- a/pkg/util/constant.go +++ b/pkg/util/constant.go @@ -61,6 +61,12 @@ const ( IdentifierKey = "identifier" HeaderDBName = "dbName" + + RoleConfigPrivileges = "privileges" + RoleConfigObjectType = "object_type" + RoleConfigObjectName = "object_name" + RoleConfigDBName = "db_name" + RoleConfigPrivilege = "privilege" ) const ( @@ -70,6 +76,7 @@ const ( var ( DefaultRoles = []string{RoleAdmin, RolePublic} + BuiltinRoles = []string{} ObjectPrivileges = map[string][]string{ commonpb.ObjectType_Collection.String(): { @@ -118,6 +125,12 @@ var ( MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateDatabase.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropDatabase.String()), MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListDatabases.String()), + + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreatePartition.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropPartition.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeShowPartitions.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeHasPartition.String()), + MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetFlushState.String()), }, commonpb.ObjectType_User.String(): { MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()), @@ -169,3 +182,12 @@ func PrivilegeNameForMetastore(name string) string { func IsAnyWord(word string) bool { return word == AnyWord } + +func IsBuiltinRole(roleName string) bool { + for _, builtinRole := range BuiltinRoles { + if builtinRole == roleName { + return true + } + } + return false +} diff --git a/pkg/util/funcutil/func.go b/pkg/util/funcutil/func.go index ffca8c19a14e9..dc69262d9fe2a 100644 --- a/pkg/util/funcutil/func.go +++ b/pkg/util/funcutil/func.go @@ -35,6 +35,7 @@ import ( "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-proto/go-api/v2/schemapb" + "github.com/milvus-io/milvus/pkg/util" "github.com/milvus-io/milvus/pkg/util/typeutil" ) @@ -93,6 +94,34 @@ func MapToJSON(m map[string]string) []byte { return bs } +func JSONToRoleDetails(mStr string) (map[string](map[string]([](map[string]string))), error) { + buffer := make(map[string](map[string]([](map[string]string))), 0) + err := json.Unmarshal([]byte(mStr), &buffer) + if err != nil { + return nil, fmt.Errorf("unmarshal `builtinRoles.Roles` failed, %w", err) + } + ret := make(map[string](map[string]([](map[string]string))), 0) + for role, privilegesJSON := range buffer { + ret[role] = make(map[string]([](map[string]string)), 0) + privilegesArray := make([]map[string]string, 0) + for _, privileges := range privilegesJSON[util.RoleConfigPrivileges] { + privilegesArray = append(privilegesArray, map[string]string{ + util.RoleConfigObjectType: privileges[util.RoleConfigObjectType], + util.RoleConfigObjectName: privileges[util.RoleConfigObjectName], + util.RoleConfigPrivilege: privileges[util.RoleConfigPrivilege], + util.RoleConfigDBName: privileges[util.RoleConfigDBName], + }) + } + ret[role]["privileges"] = privilegesArray + } + return ret, nil +} + +func RoleDetailsToJSON(m map[string](map[string]([](map[string]string)))) []byte { + bs, _ := json.Marshal(m) + return bs +} + const ( // PulsarMaxMessageSizeKey is the key of config item PulsarMaxMessageSizeKey = "maxMessageSize" diff --git a/pkg/util/funcutil/func_test.go b/pkg/util/funcutil/func_test.go index cabf80982cfb1..e9929ee8508ff 100644 --- a/pkg/util/funcutil/func_test.go +++ b/pkg/util/funcutil/func_test.go @@ -33,6 +33,7 @@ import ( "github.com/milvus-io/milvus-proto/go-api/v2/commonpb" "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb" + "github.com/milvus-io/milvus/pkg/util" ) func Test_CheckGrpcReady(t *testing.T) { @@ -89,6 +90,25 @@ func Test_ParseIndexParamsMap(t *testing.T) { assert.NotEqual(t, err, nil) } +func Test_ParseBuiltinRolesMap(t *testing.T) { + t.Run("correct format", func(t *testing.T) { + builtinRoles := `{"db_admin": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}}` + rolePrivilegesMap, err := JSONToRoleDetails(builtinRoles) + assert.Nil(t, err) + for role, privilegesJSON := range rolePrivilegesMap { + assert.Contains(t, []string{"db_admin", "db_rw", "db_ro"}, role) + for _, privileges := range privilegesJSON[util.RoleConfigPrivileges] { + assert.Equal(t, privileges[util.RoleConfigObjectType], "Global") + } + } + }) + t.Run("wrong format", func(t *testing.T) { + builtinRoles := `{"db_admin": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}` + _, err := JSONToRoleDetails(builtinRoles) + assert.NotNil(t, err) + }) +} + func TestGetAttrByKeyFromRepeatedKV(t *testing.T) { kvs := []*commonpb.KeyValuePair{ {Key: "Key1", Value: "Value1"}, diff --git a/pkg/util/funcutil/policy_test.go b/pkg/util/funcutil/policy_test.go index 03bf498884f77..93395768accf6 100644 --- a/pkg/util/funcutil/policy_test.go +++ b/pkg/util/funcutil/policy_test.go @@ -20,7 +20,7 @@ func Test_GetPrivilegeExtObj(t *testing.T) { assert.Equal(t, commonpb.ObjectPrivilege_PrivilegeLoad, privilegeExt.ObjectPrivilege) assert.Equal(t, int32(3), privilegeExt.ObjectNameIndex) - request2 := &milvuspb.CreatePartitionRequest{} + request2 := &milvuspb.GetPartitionStatisticsRequest{} _, err = GetPrivilegeExtObj(request2) assert.Error(t, err) } diff --git a/pkg/util/paramtable/component_param.go b/pkg/util/paramtable/component_param.go index dc02775ce1f1e..9fb68317509f5 100644 --- a/pkg/util/paramtable/component_param.go +++ b/pkg/util/paramtable/component_param.go @@ -69,6 +69,7 @@ type ComponentParam struct { IndexNodeCfg indexNodeConfig HTTPCfg httpConfig LogCfg logConfig + RoleCfg roleConfig RootCoordGrpcServerCfg GrpcServerConfig ProxyGrpcServerCfg GrpcServerConfig @@ -116,6 +117,7 @@ func (p *ComponentParam) init(bt *BaseTable) { p.IndexNodeCfg.init(bt) p.HTTPCfg.init(bt) p.LogCfg.init(bt) + p.RoleCfg.init(bt) p.RootCoordGrpcServerCfg.Init("rootCoord", bt) p.ProxyGrpcServerCfg.Init("proxy", bt) diff --git a/pkg/util/paramtable/param_item.go b/pkg/util/paramtable/param_item.go index 6a522dfdd33ab..4fe1c6f2fdf0a 100644 --- a/pkg/util/paramtable/param_item.go +++ b/pkg/util/paramtable/param_item.go @@ -141,6 +141,10 @@ func (pi *ParamItem) GetAsJSONMap() map[string]string { return getAndConvert(pi.GetValue(), funcutil.JSONToMap, nil) } +func (pi *ParamItem) GetAsRoleDetails() map[string](map[string]([](map[string]string))) { + return getAndConvert(pi.GetValue(), funcutil.JSONToRoleDetails, nil) +} + type CompositeParamItem struct { Items []*ParamItem Format func(map[string]string) string diff --git a/pkg/util/paramtable/role_param.go b/pkg/util/paramtable/role_param.go new file mode 100644 index 0000000000000..4be6b6a8a02a1 --- /dev/null +++ b/pkg/util/paramtable/role_param.go @@ -0,0 +1,45 @@ +package paramtable + +import ( + "github.com/milvus-io/milvus/pkg/config" + "github.com/milvus-io/milvus/pkg/util/funcutil" +) + +type roleConfig struct { + Enabled ParamItem `refreshable:"false"` + Roles ParamItem `refreshable:"false"` +} + +func (p *roleConfig) init(base *BaseTable) { + p.Enabled = ParamItem{ + Key: "builtinRoles.enable", + DefaultValue: "false", + Version: "2.3.4", + Doc: "Whether to init builtin roles", + Export: true, + } + p.Enabled.Init(base.mgr) + + p.Roles = ParamItem{ + Key: "builtinRoles.roles", + DefaultValue: `{}`, + Version: "2.3.4", + Doc: "what builtin roles should be init", + Export: true, + } + p.Roles.Init(base.mgr) + + p.panicIfNotValid(base.mgr) +} + +func (p *roleConfig) panicIfNotValid(mgr *config.Manager) { + if p.Enabled.GetAsBool() { + m := p.Roles.GetAsRoleDetails() + if m == nil { + panic("builtinRoles.roles not invalid, should be json format") + } + + j := funcutil.RoleDetailsToJSON(m) + mgr.SetConfig("builtinRoles.roles", string(j)) + } +} diff --git a/pkg/util/paramtable/role_param_test.go b/pkg/util/paramtable/role_param_test.go new file mode 100644 index 0000000000000..844fffa78a5e1 --- /dev/null +++ b/pkg/util/paramtable/role_param_test.go @@ -0,0 +1,57 @@ +package paramtable + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/milvus-io/milvus/pkg/config" +) + +func TestRoleConfig_Init(t *testing.T) { + params := ComponentParam{} + params.Init(NewBaseTable(SkipRemote(true))) + cfg := ¶ms.RoleCfg + assert.Equal(t, cfg.Enabled.GetAsBool(), false) + assert.Equal(t, cfg.Roles.GetValue(), "{}") + assert.Equal(t, len(cfg.Roles.GetAsJSONMap()), 0) +} + +func TestRoleConfig_Invalid(t *testing.T) { + t.Run("valid roles", func(t *testing.T) { + mgr := config.NewManager() + mgr.SetConfig("builtinRoles.enable", "true") + mgr.SetConfig("builtinRoles.roles", `{"db_admin": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}}`) + p := &roleConfig{ + Enabled: ParamItem{ + Key: "builtinRoles.enable", + }, + Roles: ParamItem{ + Key: "builtinRoles.roles", + }, + } + p.Enabled.Init(mgr) + p.Roles.Init(mgr) + assert.NotPanics(t, func() { + p.panicIfNotValid(mgr) + }) + }) + t.Run("invalid roles", func(t *testing.T) { + mgr := config.NewManager() + mgr.SetConfig("builtinRoles.enable", "true") + mgr.SetConfig("builtinRoles.roles", `{"db_admin": {"privileges": {"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}}}`) + p := &roleConfig{ + Enabled: ParamItem{ + Key: "builtinRoles.enable", + }, + Roles: ParamItem{ + Key: "builtinRoles.roles", + }, + } + p.Enabled.Init(mgr) + p.Roles.Init(mgr) + assert.Panics(t, func() { + p.panicIfNotValid(mgr) + }) + }) +}