mirror of https://github.com/milvus-io/milvus.git
enhance: add 3 builtin roles (#28961)
issue: #28960 [milvus-proto #212](https://github.com/milvus-io/milvus-proto/issues/212) add new configuration: builtinRoles user can define roles in config file: `milvus.yaml` there is an example: 1. db_ro, only have read privileges, include load 2. db_rw, read and write privileges, include create/drop/rename collection 3. db_admin, not only read and write privileges, but also user administration Signed-off-by: PowderLi <min.li@zilliz.com>pull/29293/head
parent
a602171d06
commit
bcd6865b29
2
go.mod
2
go.mod
|
@ -23,7 +23,7 @@ require (
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||||
github.com/klauspost/compress v1.16.7
|
github.com/klauspost/compress v1.16.7
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
|
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/milvus-io/milvus/pkg v0.0.1
|
||||||
github.com/minio/minio-go/v7 v7.0.61
|
github.com/minio/minio-go/v7 v7.0.61
|
||||||
github.com/prometheus/client_golang v1.14.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
|
|
2
go.sum
2
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/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 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.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 h1:UYJ7JB+QlMOoFHNdd8mUa3/lV63t9dnBX7ILXmEEWPY=
|
||||||
github.com/milvus-io/milvus-storage/go v0.0.0-20231109072809-1cd7b0866092/go.mod h1:GPETMcTZq1gLY1WA6Na5kiNAKnq8SEMMiVKUZrM3sho=
|
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=
|
github.com/milvus-io/pulsar-client-go v0.6.10 h1:eqpJjU+/QX0iIhEo3nhOqMNXL+TyInAs1IAHZCrCM/A=
|
||||||
|
|
|
@ -821,6 +821,11 @@ func (s *Server) CreateIndex(ctx context.Context, request *milvuspb.CreateIndexR
|
||||||
return s.proxy.CreateIndex(ctx, request)
|
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
|
// DropIndex notifies Proxy to drop index
|
||||||
func (s *Server) DropIndex(ctx context.Context, request *milvuspb.DropIndexRequest) (*commonpb.Status, error) {
|
func (s *Server) DropIndex(ctx context.Context, request *milvuspb.DropIndexRequest) (*commonpb.Status, error) {
|
||||||
return s.proxy.DropIndex(ctx, request)
|
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)
|
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) {
|
func (s *Server) Flush(ctx context.Context, request *milvuspb.FlushRequest) (*milvuspb.FlushResponse, error) {
|
||||||
return s.proxy.Flush(ctx, request)
|
return s.proxy.Flush(ctx, request)
|
||||||
}
|
}
|
||||||
|
|
|
@ -639,6 +639,11 @@ func (_c *MockProxy_CreateIndex_Call) RunAndReturn(run func(context.Context, *mi
|
||||||
return _c
|
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
|
// CreatePartition provides a mock function with given fields: _a0, _a1
|
||||||
func (_m *MockProxy) CreatePartition(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) {
|
func (_m *MockProxy) CreatePartition(_a0 context.Context, _a1 *milvuspb.CreatePartitionRequest) (*commonpb.Status, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1)
|
||||||
|
@ -4555,6 +4560,11 @@ func (_c *MockProxy_Search_Call) RunAndReturn(run func(context.Context, *milvusp
|
||||||
return _c
|
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
|
// SelectGrant provides a mock function with given fields: _a0, _a1
|
||||||
func (_m *MockProxy) SelectGrant(_a0 context.Context, _a1 *milvuspb.SelectGrantRequest) (*milvuspb.SelectGrantResponse, error) {
|
func (_m *MockProxy) SelectGrant(_a0 context.Context, _a1 *milvuspb.SelectGrantRequest) (*milvuspb.SelectGrantResponse, error) {
|
||||||
ret := _m.Called(_a0, _a1)
|
ret := _m.Called(_a0, _a1)
|
||||||
|
|
|
@ -1814,6 +1814,11 @@ func (node *Proxy) CreateIndex(ctx context.Context, request *milvuspb.CreateInde
|
||||||
return cit.result, nil
|
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.
|
// 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) {
|
func (node *Proxy) DescribeIndex(ctx context.Context, request *milvuspb.DescribeIndexRequest) (*milvuspb.DescribeIndexResponse, error) {
|
||||||
if err := merr.CheckHealthy(node.GetStateCode()); err != nil {
|
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
|
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) {
|
func (node *Proxy) getVectorPlaceholderGroupForSearchByPks(ctx context.Context, request *milvuspb.SearchRequest) ([]byte, error) {
|
||||||
placeholderGroup := &commonpb.PlaceholderGroup{}
|
placeholderGroup := &commonpb.PlaceholderGroup{}
|
||||||
err := proto.Unmarshal(request.PlaceholderGroup, 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
|
return merr.Status(err), nil
|
||||||
}
|
}
|
||||||
if IsDefaultRole(req.RoleName) {
|
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
|
return merr.Status(err), nil
|
||||||
}
|
}
|
||||||
result, err := node.rootCoord.DropRole(ctx, req)
|
result, err := node.rootCoord.DropRole(ctx, req)
|
||||||
|
|
|
@ -168,7 +168,7 @@ func PrivilegeInterceptor(ctx context.Context, req interface{}) (context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("permission deny", zap.Strings("roles", roleNames))
|
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,
|
// isCurUserObject Determine whether it is an Object of type User that operates on its own user information,
|
||||||
|
|
|
@ -613,6 +613,43 @@ func (c *Core) initRbac() error {
|
||||||
return errors.Wrap(err, "failed to grant collection privilege")
|
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
|
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 {
|
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
|
||||||
return merr.Status(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 {
|
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"
|
errMsg := "not found the role, maybe the role isn't existed or internal system error"
|
||||||
ctxLog.Warn(errMsg, zap.Error(err))
|
ctxLog.Warn(errMsg, zap.Error(err))
|
||||||
|
|
|
@ -49,6 +49,7 @@ import (
|
||||||
"github.com/milvus-io/milvus/internal/util/importutil"
|
"github.com/milvus-io/milvus/internal/util/importutil"
|
||||||
"github.com/milvus-io/milvus/internal/util/sessionutil"
|
"github.com/milvus-io/milvus/internal/util/sessionutil"
|
||||||
"github.com/milvus-io/milvus/pkg/common"
|
"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/etcd"
|
||||||
"github.com/milvus-io/milvus/pkg/util/funcutil"
|
"github.com/milvus-io/milvus/pkg/util/funcutil"
|
||||||
"github.com/milvus-io/milvus/pkg/util/merr"
|
"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) {
|
func TestCore_Stop(t *testing.T) {
|
||||||
t.Run("abnormal stop before component is ready", func(t *testing.T) {
|
t.Run("abnormal stop before component is ready", func(t *testing.T) {
|
||||||
c := &Core{}
|
c := &Core{}
|
||||||
|
|
|
@ -13,7 +13,7 @@ require (
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||||
github.com/klauspost/compress v1.16.5
|
github.com/klauspost/compress v1.16.5
|
||||||
github.com/lingdor/stackerror v0.0.0-20191119040541-976d8885ed76
|
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-server/v2 v2.9.17
|
||||||
github.com/nats-io/nats.go v1.24.0
|
github.com/nats-io/nats.go v1.24.0
|
||||||
github.com/panjf2000/ants/v2 v2.7.2
|
github.com/panjf2000/ants/v2 v2.7.2
|
||||||
|
|
|
@ -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.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 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.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 h1:eqpJjU+/QX0iIhEo3nhOqMNXL+TyInAs1IAHZCrCM/A=
|
||||||
github.com/milvus-io/pulsar-client-go v0.6.10/go.mod h1:lQqCkgwDF8YFYjKA+zOheTk1tev2B+bKj5j7+nm8M1w=
|
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=
|
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||||
|
|
|
@ -61,6 +61,12 @@ const (
|
||||||
|
|
||||||
IdentifierKey = "identifier"
|
IdentifierKey = "identifier"
|
||||||
HeaderDBName = "dbName"
|
HeaderDBName = "dbName"
|
||||||
|
|
||||||
|
RoleConfigPrivileges = "privileges"
|
||||||
|
RoleConfigObjectType = "object_type"
|
||||||
|
RoleConfigObjectName = "object_name"
|
||||||
|
RoleConfigDBName = "db_name"
|
||||||
|
RoleConfigPrivilege = "privilege"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -70,6 +76,7 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultRoles = []string{RoleAdmin, RolePublic}
|
DefaultRoles = []string{RoleAdmin, RolePublic}
|
||||||
|
BuiltinRoles = []string{}
|
||||||
|
|
||||||
ObjectPrivileges = map[string][]string{
|
ObjectPrivileges = map[string][]string{
|
||||||
commonpb.ObjectType_Collection.String(): {
|
commonpb.ObjectType_Collection.String(): {
|
||||||
|
@ -118,6 +125,12 @@ var (
|
||||||
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateDatabase.String()),
|
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreateDatabase.String()),
|
||||||
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropDatabase.String()),
|
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropDatabase.String()),
|
||||||
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListDatabases.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(): {
|
commonpb.ObjectType_User.String(): {
|
||||||
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()),
|
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()),
|
||||||
|
@ -169,3 +182,12 @@ func PrivilegeNameForMetastore(name string) string {
|
||||||
func IsAnyWord(word string) bool {
|
func IsAnyWord(word string) bool {
|
||||||
return word == AnyWord
|
return word == AnyWord
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsBuiltinRole(roleName string) bool {
|
||||||
|
for _, builtinRole := range BuiltinRoles {
|
||||||
|
if builtinRole == roleName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import (
|
||||||
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
"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/milvuspb"
|
||||||
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
|
"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"
|
"github.com/milvus-io/milvus/pkg/util/typeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,6 +94,34 @@ func MapToJSON(m map[string]string) []byte {
|
||||||
return bs
|
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 (
|
const (
|
||||||
// PulsarMaxMessageSizeKey is the key of config item
|
// PulsarMaxMessageSizeKey is the key of config item
|
||||||
PulsarMaxMessageSizeKey = "maxMessageSize"
|
PulsarMaxMessageSizeKey = "maxMessageSize"
|
||||||
|
|
|
@ -33,6 +33,7 @@ import (
|
||||||
|
|
||||||
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
|
"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/milvuspb"
|
||||||
|
"github.com/milvus-io/milvus/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_CheckGrpcReady(t *testing.T) {
|
func Test_CheckGrpcReady(t *testing.T) {
|
||||||
|
@ -89,6 +90,25 @@ func Test_ParseIndexParamsMap(t *testing.T) {
|
||||||
assert.NotEqual(t, err, nil)
|
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) {
|
func TestGetAttrByKeyFromRepeatedKV(t *testing.T) {
|
||||||
kvs := []*commonpb.KeyValuePair{
|
kvs := []*commonpb.KeyValuePair{
|
||||||
{Key: "Key1", Value: "Value1"},
|
{Key: "Key1", Value: "Value1"},
|
||||||
|
|
|
@ -20,7 +20,7 @@ func Test_GetPrivilegeExtObj(t *testing.T) {
|
||||||
assert.Equal(t, commonpb.ObjectPrivilege_PrivilegeLoad, privilegeExt.ObjectPrivilege)
|
assert.Equal(t, commonpb.ObjectPrivilege_PrivilegeLoad, privilegeExt.ObjectPrivilege)
|
||||||
assert.Equal(t, int32(3), privilegeExt.ObjectNameIndex)
|
assert.Equal(t, int32(3), privilegeExt.ObjectNameIndex)
|
||||||
|
|
||||||
request2 := &milvuspb.CreatePartitionRequest{}
|
request2 := &milvuspb.GetPartitionStatisticsRequest{}
|
||||||
_, err = GetPrivilegeExtObj(request2)
|
_, err = GetPrivilegeExtObj(request2)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,7 @@ type ComponentParam struct {
|
||||||
IndexNodeCfg indexNodeConfig
|
IndexNodeCfg indexNodeConfig
|
||||||
HTTPCfg httpConfig
|
HTTPCfg httpConfig
|
||||||
LogCfg logConfig
|
LogCfg logConfig
|
||||||
|
RoleCfg roleConfig
|
||||||
|
|
||||||
RootCoordGrpcServerCfg GrpcServerConfig
|
RootCoordGrpcServerCfg GrpcServerConfig
|
||||||
ProxyGrpcServerCfg GrpcServerConfig
|
ProxyGrpcServerCfg GrpcServerConfig
|
||||||
|
@ -116,6 +117,7 @@ func (p *ComponentParam) init(bt *BaseTable) {
|
||||||
p.IndexNodeCfg.init(bt)
|
p.IndexNodeCfg.init(bt)
|
||||||
p.HTTPCfg.init(bt)
|
p.HTTPCfg.init(bt)
|
||||||
p.LogCfg.init(bt)
|
p.LogCfg.init(bt)
|
||||||
|
p.RoleCfg.init(bt)
|
||||||
|
|
||||||
p.RootCoordGrpcServerCfg.Init("rootCoord", bt)
|
p.RootCoordGrpcServerCfg.Init("rootCoord", bt)
|
||||||
p.ProxyGrpcServerCfg.Init("proxy", bt)
|
p.ProxyGrpcServerCfg.Init("proxy", bt)
|
||||||
|
|
|
@ -141,6 +141,10 @@ func (pi *ParamItem) GetAsJSONMap() map[string]string {
|
||||||
return getAndConvert(pi.GetValue(), funcutil.JSONToMap, nil)
|
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 {
|
type CompositeParamItem struct {
|
||||||
Items []*ParamItem
|
Items []*ParamItem
|
||||||
Format func(map[string]string) string
|
Format func(map[string]string) string
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue