enhance: Support custom privilege group for RBAC (#37087)

issue: #37031

---------

Signed-off-by: shaoting-huang <shaoting.huang@zilliz.com>
pull/37558/head
sthuang 2024-11-09 08:44:28 +08:00 committed by GitHub
parent ff9bdf7029
commit 70605cf5b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 2920 additions and 209 deletions

View File

@ -13,7 +13,6 @@ require (
github.com/stretchr/testify v1.9.0
github.com/tidwall/gjson v1.17.1
go.uber.org/atomic v1.10.0
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
)
@ -109,6 +108,7 @@ require (
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.20.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect

3
go.mod
View File

@ -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.20241106083218-5de5d0cfb1c1
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620
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
@ -138,6 +138,7 @@ require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/flatbuffers v2.0.8+incompatible // indirect
github.com/google/s2a-go v0.1.7 // indirect

8
go.sum
View File

@ -363,8 +363,9 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -627,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.20241106083218-5de5d0cfb1c1 h1:GFS5AxKPcEstcfJgMGxRH+l/mKA0kK1sHDOxnOqMnoA=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241106083218-5de5d0cfb1c1/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620 h1:0IWUDtDloift7cQHalhdjuVkL/3qSeiXFqR7MofZBkg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620/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=
@ -1323,6 +1324,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=

View File

@ -686,6 +686,22 @@ func (m *mockRootCoordClient) ListPolicy(ctx context.Context, in *internalpb.Lis
return &internalpb.ListPolicyResponse{Status: &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success}}, nil
}
func (m *mockRootCoordClient) CreatePrivilegeGroup(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
panic("implement me")
}
func (m *mockRootCoordClient) DropPrivilegeGroup(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
panic("implement me")
}
func (m *mockRootCoordClient) ListPrivilegeGroups(ctx context.Context, req *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error) {
panic("implement me")
}
func (m *mockRootCoordClient) OperatePrivilegeGroup(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
panic("implement me")
}
type mockHandler struct {
meta *meta
}

View File

@ -20,10 +20,9 @@ import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/milvus-io/milvus/pkg/config"
"github.com/milvus-io/milvus/pkg/util/paramtable"
"github.com/stretchr/testify/assert"
)
func TestResizePools(t *testing.T) {

View File

@ -9,14 +9,15 @@ import (
// v2
const (
// --- category ---
CollectionCategory = "/collections/"
EntityCategory = "/entities/"
PartitionCategory = "/partitions/"
UserCategory = "/users/"
RoleCategory = "/roles/"
IndexCategory = "/indexes/"
AliasCategory = "/aliases/"
ImportJobCategory = "/jobs/import/"
CollectionCategory = "/collections/"
EntityCategory = "/entities/"
PartitionCategory = "/partitions/"
UserCategory = "/users/"
RoleCategory = "/roles/"
IndexCategory = "/indexes/"
AliasCategory = "/aliases/"
ImportJobCategory = "/jobs/import/"
PrivilegeGroupCategory = "/privilege_groups/"
ListAction = "list"
HasAction = "has"
@ -37,13 +38,15 @@ const (
AdvancedSearchAction = "advanced_search"
HybridSearchAction = "hybrid_search"
UpdatePasswordAction = "update_password"
GrantRoleAction = "grant_role"
RevokeRoleAction = "revoke_role"
GrantPrivilegeAction = "grant_privilege"
RevokePrivilegeAction = "revoke_privilege"
AlterAction = "alter"
GetProgressAction = "get_progress" // deprecated, keep it for compatibility, use `/v2/vectordb/jobs/import/describe` instead
UpdatePasswordAction = "update_password"
GrantRoleAction = "grant_role"
RevokeRoleAction = "revoke_role"
GrantPrivilegeAction = "grant_privilege"
RevokePrivilegeAction = "revoke_privilege"
AlterAction = "alter"
GetProgressAction = "get_progress" // deprecated, keep it for compatibility, use `/v2/vectordb/jobs/import/describe` instead
AddPrivilegesToGroupAction = "add_privileges_to_group"
RemovePrivilegesFromGroupAction = "remove_privileges_from_group"
)
const (
@ -125,11 +128,14 @@ const (
HTTPReturnRowCount = "rowCount"
HTTPReturnObjectType = "objectType"
HTTPReturnObjectName = "objectName"
HTTPReturnPrivilege = "privilege"
HTTPReturnGrantor = "grantor"
HTTPReturnDbName = "dbName"
HTTPReturnObjectType = "objectType"
HTTPReturnObjectName = "objectName"
HTTPReturnPrivilege = "privilege"
HTTPReturnGrantor = "grantor"
HTTPReturnDbName = "dbName"
HTTPReturnPrivilegeGroupName = "privilegeGroupName"
HTTPReturnPrivileges = "privileges"
HTTPReturnPrivilegeGroups = "privilegeGroups"
DefaultMetricType = metric.COSINE
DefaultPrimaryFieldName = "id"

View File

@ -132,6 +132,13 @@ func (h *HandlersV2) RegisterRoutesToV2(router gin.IRouter) {
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))))
// privilege group
router.POST(PrivilegeGroupCategory+CreateAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.createPrivilegeGroup))))
router.POST(PrivilegeGroupCategory+DropAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.dropPrivilegeGroup))))
router.POST(PrivilegeGroupCategory+ListAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.listPrivilegeGroups))))
router.POST(PrivilegeGroupCategory+AddPrivilegesToGroupAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.addPrivilegesToGroup))))
router.POST(PrivilegeGroupCategory+RemovePrivilegesFromGroupAction, timeoutMiddleware(wrapperPost(func() any { return &GrantReq{} }, wrapperTraceLog(h.removePrivilegesFromGroup))))
router.POST(IndexCategory+ListAction, timeoutMiddleware(wrapperPost(func() any { return &CollectionNameReq{} }, wrapperTraceLog(h.wrapperCheckDatabase(h.listIndexes)))))
router.POST(IndexCategory+DescribeAction, timeoutMiddleware(wrapperPost(func() any { return &IndexReq{} }, wrapperTraceLog(h.wrapperCheckDatabase(h.describeIndex)))))
@ -1786,6 +1793,83 @@ func (h *HandlersV2) removePrivilegeFromRole(ctx context.Context, c *gin.Context
return h.operatePrivilegeToRole(ctx, c, anyReq.(*GrantReq), milvuspb.OperatePrivilegeType_Revoke, dbName)
}
func (h *HandlersV2) createPrivilegeGroup(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
httpReq := anyReq.(*PrivilegeGroupReq)
req := &milvuspb.CreatePrivilegeGroupRequest{
GroupName: httpReq.PrivilegeGroupName,
}
resp, err := wrapperProxy(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/CreatePrivilegeGroup", func(reqCtx context.Context, req any) (interface{}, error) {
return h.proxy.CreatePrivilegeGroup(reqCtx, req.(*milvuspb.CreatePrivilegeGroupRequest))
})
if err == nil {
HTTPReturn(c, http.StatusOK, wrapperReturnDefault())
}
return resp, err
}
func (h *HandlersV2) dropPrivilegeGroup(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
httpReq := anyReq.(*PrivilegeGroupReq)
req := &milvuspb.DropPrivilegeGroupRequest{
GroupName: httpReq.PrivilegeGroupName,
}
resp, err := wrapperProxy(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/DropPrivilegeGroup", func(reqCtx context.Context, req any) (interface{}, error) {
return h.proxy.DropPrivilegeGroup(reqCtx, req.(*milvuspb.DropPrivilegeGroupRequest))
})
if err == nil {
HTTPReturn(c, http.StatusOK, wrapperReturnDefault())
}
return resp, err
}
func (h *HandlersV2) listPrivilegeGroups(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
req := &milvuspb.ListPrivilegeGroupsRequest{}
resp, err := wrapperProxy(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/ListPrivilegeGroups", func(reqCtx context.Context, req any) (interface{}, error) {
return h.proxy.ListPrivilegeGroups(reqCtx, req.(*milvuspb.ListPrivilegeGroupsRequest))
})
if err == nil {
privGroups := make([]map[string]interface{}, 0)
for _, group := range resp.(*milvuspb.ListPrivilegeGroupsResponse).PrivilegeGroups {
privileges := make([]string, len(group.Privileges))
for i, privilege := range group.Privileges {
privileges[i] = privilege.Name
}
groupInfo := map[string]interface{}{
HTTPReturnPrivilegeGroupName: group.GroupName,
HTTPReturnPrivileges: strings.Join(privileges, ","),
}
privGroups = append(privGroups, groupInfo)
}
HTTPReturn(c, http.StatusOK, gin.H{HTTPReturnCode: merr.Code(nil), HTTPReturnData: gin.H{
HTTPReturnPrivilegeGroups: privGroups,
}})
}
return resp, err
}
func (h *HandlersV2) addPrivilegesToGroup(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
return h.operatePrivilegeGroup(ctx, c, anyReq, dbName, milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup)
}
func (h *HandlersV2) removePrivilegesFromGroup(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
return h.operatePrivilegeGroup(ctx, c, anyReq, dbName, milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup)
}
func (h *HandlersV2) operatePrivilegeGroup(ctx context.Context, c *gin.Context, anyReq any, dbName string, operateType milvuspb.OperatePrivilegeGroupType) (interface{}, error) {
httpReq := anyReq.(*PrivilegeGroupReq)
req := &milvuspb.OperatePrivilegeGroupRequest{
GroupName: httpReq.PrivilegeGroupName,
Privileges: httpReq.Privileges,
Type: operateType,
}
resp, err := wrapperProxy(ctx, c, req, h.checkAuth, false, "/milvus.proto.milvus.MilvusService/OperatePrivilegeGroup", func(reqCtx context.Context, req any) (interface{}, error) {
return h.proxy.OperatePrivilegeGroup(reqCtx, req.(*milvuspb.OperatePrivilegeGroupRequest))
})
if err == nil {
HTTPReturn(c, http.StatusOK, wrapperReturnDefault())
}
return resp, err
}
func (h *HandlersV2) listIndexes(ctx context.Context, c *gin.Context, anyReq any, dbName string) (interface{}, error) {
collectionGetter, _ := anyReq.(requestutil.CollectionNameGetter)
indexNames := []string{}

View File

@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"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/merr"
)
@ -273,6 +274,11 @@ func (req *RoleReq) GetRoleName() string {
return req.RoleName
}
type PrivilegeGroupReq struct {
PrivilegeGroupName string `json:"privilegeGroupName" binding:"required"`
Privileges []*milvuspb.PrivilegeEntity `json:"privileges"`
}
type GrantReq struct {
RoleName string `json:"roleName" binding:"required"`
ObjectType string `json:"objectType" binding:"required"`

View File

@ -997,6 +997,22 @@ func (s *Server) RestoreRBAC(ctx context.Context, req *milvuspb.RestoreRBACMetaR
return s.proxy.RestoreRBAC(ctx, req)
}
func (s *Server) CreatePrivilegeGroup(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
return s.proxy.CreatePrivilegeGroup(ctx, req)
}
func (s *Server) DropPrivilegeGroup(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
return s.proxy.DropPrivilegeGroup(ctx, req)
}
func (s *Server) ListPrivilegeGroups(ctx context.Context, req *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
return s.proxy.ListPrivilegeGroups(ctx, req)
}
func (s *Server) OperatePrivilegeGroup(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
return s.proxy.OperatePrivilegeGroup(ctx, req)
}
func (s *Server) RefreshPolicyInfoCache(ctx context.Context, req *proxypb.RefreshPolicyInfoCacheRequest) (*commonpb.Status, error) {
return s.proxy.RefreshPolicyInfoCache(ctx, req)
}

View File

@ -706,3 +706,51 @@ func (c *Client) RestoreRBAC(ctx context.Context, in *milvuspb.RestoreRBACMetaRe
return client.RestoreRBAC(ctx, in)
})
}
func (c *Client) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
in = typeutil.Clone(in)
commonpbutil.UpdateMsgBase(
in.GetBase(),
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.sess.ServerID)),
)
return wrapGrpcCall(ctx, c, func(client rootcoordpb.RootCoordClient) (*commonpb.Status, error) {
return client.CreatePrivilegeGroup(ctx, in)
})
}
func (c *Client) DropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
in = typeutil.Clone(in)
commonpbutil.UpdateMsgBase(
in.GetBase(),
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.sess.ServerID)),
)
return wrapGrpcCall(ctx, c, func(client rootcoordpb.RootCoordClient) (*commonpb.Status, error) {
return client.DropPrivilegeGroup(ctx, in)
})
}
func (c *Client) ListPrivilegeGroups(ctx context.Context, in *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error) {
in = typeutil.Clone(in)
commonpbutil.UpdateMsgBase(
in.GetBase(),
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.sess.ServerID)),
)
return wrapGrpcCall(ctx, c, func(client rootcoordpb.RootCoordClient) (*milvuspb.ListPrivilegeGroupsResponse, error) {
return client.ListPrivilegeGroups(ctx, in)
})
}
func (c *Client) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
in = typeutil.Clone(in)
commonpbutil.UpdateMsgBase(
in.GetBase(),
commonpbutil.FillMsgBaseFromClient(paramtable.GetNodeID(), commonpbutil.WithTargetID(c.sess.ServerID)),
)
return wrapGrpcCall(ctx, c, func(client rootcoordpb.RootCoordClient) (*commonpb.Status, error) {
return client.OperatePrivilegeGroup(ctx, in)
})
}

View File

@ -240,10 +240,38 @@ func Test_NewClient(t *testing.T) {
r, err := client.ListDatabases(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.AlterCollection(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.AlterDatabase(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.BackupRBAC(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.RestoreRBAC(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.CreatePrivilegeGroup(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.DropPrivilegeGroup(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.ListPrivilegeGroups(ctx, nil)
retCheck(retNotNil, r, err)
}
{
r, err := client.OperatePrivilegeGroup(ctx, nil)
retCheck(retNotNil, r, err)
}
}
client.(*Client).grpcClient = &mock.GRPCClientBase[rootcoordpb.RootCoordClient]{

View File

@ -550,3 +550,19 @@ func (s *Server) BackupRBAC(ctx context.Context, request *milvuspb.BackupRBACMet
func (s *Server) RestoreRBAC(ctx context.Context, request *milvuspb.RestoreRBACMetaRequest) (*commonpb.Status, error) {
return s.rootCoord.RestoreRBAC(ctx, request)
}
func (s *Server) CreatePrivilegeGroup(ctx context.Context, request *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
return s.rootCoord.CreatePrivilegeGroup(ctx, request)
}
func (s *Server) DropPrivilegeGroup(ctx context.Context, request *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
return s.rootCoord.DropPrivilegeGroup(ctx, request)
}
func (s *Server) ListPrivilegeGroups(ctx context.Context, request *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
return s.rootCoord.ListPrivilegeGroups(ctx, request)
}
func (s *Server) OperatePrivilegeGroup(ctx context.Context, request *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
return s.rootCoord.OperatePrivilegeGroup(ctx, request)
}

View File

@ -86,6 +86,11 @@ type RootCoordCatalog interface {
BackupRBAC(ctx context.Context, tenant string) (*milvuspb.RBACMeta, error)
RestoreRBAC(ctx context.Context, tenant string, meta *milvuspb.RBACMeta) error
GetPrivilegeGroup(ctx context.Context, groupName string) (*milvuspb.PrivilegeGroupInfo, error)
DropPrivilegeGroup(ctx context.Context, groupName string) error
SavePrivilegeGroup(ctx context.Context, data *milvuspb.PrivilegeGroupInfo) error
ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error)
Close()
}

View File

@ -1332,10 +1332,16 @@ func (kc *Catalog) BackupRBAC(ctx context.Context, tenant string) (*milvuspb.RBA
grantsEntity = append(grantsEntity, grants...)
}
privGroups, err := kc.ListPrivilegeGroups(ctx)
if err != nil {
return nil, err
}
return &milvuspb.RBACMeta{
Users: userInfos,
Roles: roleEntity,
Grants: grantsEntity,
Users: userInfos,
Roles: roleEntity,
Grants: grantsEntity,
PrivilegeGroups: privGroups,
}, nil
}
@ -1344,6 +1350,7 @@ func (kc *Catalog) RestoreRBAC(ctx context.Context, tenant string, meta *milvusp
needRollbackUser := make([]*milvuspb.UserInfo, 0)
needRollbackRole := make([]*milvuspb.RoleEntity, 0)
needRollbackGrants := make([]*milvuspb.GrantEntity, 0)
needRollbackPrivilegeGroups := make([]*milvuspb.PrivilegeGroupInfo, 0)
defer func() {
if err != nil {
log.Warn("failed to restore rbac, try to rollback", zap.Error(err))
@ -1370,6 +1377,14 @@ func (kc *Catalog) RestoreRBAC(ctx context.Context, tenant string, meta *milvusp
log.Warn("failed to rollback users after restore failed", zap.Error(err))
}
}
// roll back privilege group
for _, group := range needRollbackPrivilegeGroups {
err = kc.DropPrivilegeGroup(ctx, group.GroupName)
if err != nil {
log.Warn("failed to rollback privilege groups after restore failed", zap.Error(err))
}
}
}
}()
@ -1392,9 +1407,42 @@ func (kc *Catalog) RestoreRBAC(ctx context.Context, tenant string, meta *milvusp
needRollbackRole = append(needRollbackRole, role)
}
// restore grant
// restore privilege group
existPrivGroups, err := kc.ListPrivilegeGroups(ctx)
if err != nil {
return err
}
existPrivGroupMap := lo.SliceToMap(existPrivGroups, func(entity *milvuspb.PrivilegeGroupInfo) (string, struct{}) { return entity.GroupName, struct{}{} })
for _, group := range meta.PrivilegeGroups {
if _, ok := existPrivGroupMap[group.GroupName]; ok {
log.Warn("failed to restore, privilege group already exists", zap.String("group", group.GroupName))
err = errors.Newf("privilege group [%s] already exists", group.GroupName)
return err
}
err = kc.SavePrivilegeGroup(ctx, group)
if err != nil {
return err
}
needRollbackPrivilegeGroups = append(needRollbackPrivilegeGroups, group)
}
// restore grant, list latest privilege group first
existPrivGroups, err = kc.ListPrivilegeGroups(ctx)
if err != nil {
return err
}
existPrivGroupMap = lo.SliceToMap(existPrivGroups, func(entity *milvuspb.PrivilegeGroupInfo) (string, struct{}) { return entity.GroupName, struct{}{} })
for _, grant := range meta.Grants {
grant.Grantor.Privilege.Name = util.PrivilegeNameForMetastore(grant.Grantor.Privilege.Name)
privName := grant.Grantor.Privilege.Name
if util.IsPrivilegeNameDefined(privName) {
grant.Grantor.Privilege.Name = util.PrivilegeNameForMetastore(privName)
} else if _, ok := existPrivGroupMap[privName]; ok {
grant.Grantor.Privilege.Name = util.PrivilegeGroupNameForMetastore(privName)
} else {
log.Warn("failed to restore, privilege group does not exist", zap.String("group", privName))
err = errors.Newf("privilege group [%s] does not exist", privName)
return err
}
err = kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Grant)
if err != nil {
return err
@ -1439,6 +1487,72 @@ func (kc *Catalog) RestoreRBAC(ctx context.Context, tenant string, meta *milvusp
return err
}
func (kc *Catalog) GetPrivilegeGroup(ctx context.Context, groupName string) (*milvuspb.PrivilegeGroupInfo, error) {
k := BuildPrivilegeGroupkey(groupName)
val, err := kc.Txn.Load(k)
if err != nil {
if errors.Is(err, merr.ErrIoKeyNotFound) {
return nil, fmt.Errorf("privilege group [%s] does not exist", groupName)
}
log.Error("failed to load privilege group", zap.String("group", groupName), zap.Error(err))
return nil, err
}
privGroupInfo := &milvuspb.PrivilegeGroupInfo{}
err = proto.Unmarshal([]byte(val), privGroupInfo)
if err != nil {
log.Error("failed to unmarshal privilege group info", zap.Error(err))
return nil, err
}
return privGroupInfo, nil
}
func (kc *Catalog) DropPrivilegeGroup(ctx context.Context, groupName string) error {
k := BuildPrivilegeGroupkey(groupName)
err := kc.Txn.Remove(k)
if err != nil {
log.Warn("fail to drop privilege group", zap.String("key", k), zap.Error(err))
return err
}
return nil
}
func (kc *Catalog) SavePrivilegeGroup(ctx context.Context, data *milvuspb.PrivilegeGroupInfo) error {
k := BuildPrivilegeGroupkey(data.GroupName)
groupInfo := &milvuspb.PrivilegeGroupInfo{
GroupName: data.GroupName,
Privileges: lo.Uniq(data.Privileges),
}
v, err := proto.Marshal(groupInfo)
if err != nil {
log.Error("failed to marshal privilege group info", zap.Error(err))
return err
}
if err = kc.Txn.Save(k, string(v)); err != nil {
log.Warn("fail to put privilege group", zap.String("key", k), zap.Error(err))
return err
}
return nil
}
func (kc *Catalog) ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
_, vals, err := kc.Txn.LoadWithPrefix(PrivilegeGroupPrefix)
if err != nil {
log.Error("failed to list privilege groups", zap.String("prefix", PrivilegeGroupPrefix), zap.Error(err))
return nil, err
}
privGroups := make([]*milvuspb.PrivilegeGroupInfo, 0, len(vals))
for _, val := range vals {
privGroupInfo := &milvuspb.PrivilegeGroupInfo{}
err = proto.Unmarshal([]byte(val), privGroupInfo)
if err != nil {
log.Error("failed to unmarshal privilege group info", zap.Error(err))
return nil, err
}
privGroups = append(privGroups, privGroupInfo)
}
return privGroups, nil
}
func (kc *Catalog) Close() {
// do nothing
}

View File

@ -9,6 +9,7 @@ import (
"testing"
"github.com/cockroachdb/errors"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@ -2701,6 +2702,11 @@ func TestRBAC_Backup(t *testing.T) {
})
c.AlterUserRole(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: "user1"}, &milvuspb.RoleEntity{Name: "role1"}, milvuspb.OperateUserRoleType_AddUserToRole)
c.SavePrivilegeGroup(ctx, &milvuspb.PrivilegeGroupInfo{
GroupName: "custom_group",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}},
})
// test backup success
backup, err := c.BackupRBAC(ctx, util.DefaultTenant)
assert.NoError(t, err)
@ -2711,6 +2717,9 @@ func TestRBAC_Backup(t *testing.T) {
assert.Equal(t, "user1", backup.Users[0].User)
assert.Equal(t, 1, len(backup.Users[0].Roles))
assert.Equal(t, 1, len(backup.Roles))
assert.Equal(t, 1, len(backup.PrivilegeGroups))
assert.Equal(t, "custom_group", backup.PrivilegeGroups[0].GroupName)
assert.Equal(t, "CreateCollection", backup.PrivilegeGroups[0].Privileges[0].Name)
}
func TestRBAC_Restore(t *testing.T) {
@ -2756,10 +2765,17 @@ func TestRBAC_Restore(t *testing.T) {
DbName: util.DefaultDBName,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "user1"},
Privilege: &milvuspb.PrivilegeEntity{Name: "PrivilegeLoad"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Load"},
},
},
},
PrivilegeGroups: []*milvuspb.PrivilegeGroupInfo{
{
GroupName: "custom_group",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}},
},
},
}
// test restore success
err := c.RestoreRBAC(ctx, util.DefaultTenant, rbacMeta)
@ -2785,6 +2801,13 @@ func TestRBAC_Restore(t *testing.T) {
assert.Equal(t, "obj_name1", grants[0].ObjectName)
assert.Equal(t, "role1", grants[0].Role.Name)
assert.Equal(t, "user1", grants[0].Grantor.User.Name)
assert.Equal(t, "Load", grants[0].Grantor.Privilege.Name)
// check privilege group
privGroups, err := c.ListPrivilegeGroups(ctx)
assert.NoError(t, err)
assert.Len(t, privGroups, 1)
assert.Equal(t, "custom_group", privGroups[0].GroupName)
assert.Equal(t, "CreateCollection", privGroups[0].Privileges[0].Name)
rbacMeta2 := &milvuspb.RBACMeta{
Users: []*milvuspb.UserInfo{
@ -2821,10 +2844,17 @@ func TestRBAC_Restore(t *testing.T) {
DbName: util.DefaultDBName,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "user2"},
Privilege: &milvuspb.PrivilegeEntity{Name: "PrivilegeLoad"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Load"},
},
},
},
PrivilegeGroups: []*milvuspb.PrivilegeGroupInfo{
{
GroupName: "custom_group2",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "DropCollection"}},
},
},
}
// test restore failed and roll back
@ -2846,6 +2876,145 @@ func TestRBAC_Restore(t *testing.T) {
})
assert.NoError(t, err)
assert.Len(t, grants, 1)
assert.Equal(t, grants[0].Grantor.Privilege.Name, "Load")
// check privilege group
privGroups, err = c.ListPrivilegeGroups(ctx)
assert.NoError(t, err)
assert.Len(t, privGroups, 1)
assert.Equal(t, "custom_group", privGroups[0].GroupName)
assert.Equal(t, "CreateCollection", privGroups[0].Privileges[0].Name)
}
func TestRBAC_PrivilegeGroup(t *testing.T) {
ctx := context.TODO()
group1 := "group1"
group2 := "group2"
key1 := BuildPrivilegeGroupkey(group1)
key2 := BuildPrivilegeGroupkey(group2)
privGroupInfo1 := &milvuspb.PrivilegeGroupInfo{GroupName: group1, Privileges: []*milvuspb.PrivilegeEntity{{Name: "priv10"}, {Name: "priv11"}}}
privGroupInfo2 := &milvuspb.PrivilegeGroupInfo{GroupName: group2, Privileges: []*milvuspb.PrivilegeEntity{{Name: "priv20"}, {Name: "priv21"}}}
v1, _ := proto.Marshal(privGroupInfo1)
v2, _ := proto.Marshal(privGroupInfo2)
t.Run("test GetPrivilegeGroup", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = &Catalog{Txn: kvmock}
)
kvmock.EXPECT().Load(key1).Return(string(v1), nil)
kvmock.EXPECT().Load(key2).Return("", merr.ErrIoKeyNotFound)
tests := []struct {
description string
expectedErr error
groupName string
expectedPrivileges []string
}{
{"group not found", fmt.Errorf("privilege group [%s] does not exist", group2), group2, nil},
{"valid group", nil, group1, []string{"priv10", "priv11"}},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
group, err := c.GetPrivilegeGroup(ctx, test.groupName)
if test.expectedErr != nil {
assert.Error(t, err, test.expectedErr)
} else {
assert.NoError(t, err)
assert.ElementsMatch(t, getPrivilegeNames(group.Privileges), test.expectedPrivileges)
}
})
}
})
t.Run("test DropPrivilegeGroup", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = &Catalog{Txn: kvmock}
)
kvmock.EXPECT().Remove(key1).Return(nil)
kvmock.EXPECT().Remove(key2).Return(errors.New("Mock remove failure"))
tests := []struct {
description string
isValid bool
groupName string
}{
{"valid group", true, group1},
{"remove failure", false, group2},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.DropPrivilegeGroup(ctx, test.groupName)
if test.isValid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
})
t.Run("test SavePrivilegeGroup", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = &Catalog{Txn: kvmock}
)
kvmock.EXPECT().Save(key1, mock.Anything).Return(nil)
kvmock.EXPECT().Save(key2, mock.Anything).Return(nil)
tests := []struct {
description string
isValid bool
group *milvuspb.PrivilegeGroupInfo
}{
{"valid group with existing key", true, &milvuspb.PrivilegeGroupInfo{GroupName: group1, Privileges: []*milvuspb.PrivilegeEntity{{Name: "priv10"}, {Name: "priv11"}}}},
{"valid group without existing key", true, &milvuspb.PrivilegeGroupInfo{GroupName: group2, Privileges: []*milvuspb.PrivilegeEntity{{Name: "priv10"}, {Name: "priv11"}}}},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.SavePrivilegeGroup(ctx, test.group)
if test.isValid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
})
t.Run("test ListPrivilegeGroups", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = &Catalog{Txn: kvmock}
)
kvmock.EXPECT().LoadWithPrefix(PrivilegeGroupPrefix).Return(
[]string{key1, key2},
[]string{string(v1), string(v2)},
nil,
)
groups, err := c.ListPrivilegeGroups(ctx)
assert.NoError(t, err)
groupNames := lo.Map(groups, func(g *milvuspb.PrivilegeGroupInfo, _ int) string {
return g.GroupName
})
assert.ElementsMatch(t, groupNames, []string{group1, group2})
assert.ElementsMatch(t, getPrivilegeNames(groups[0].Privileges), []string{"priv10", "priv11"})
assert.ElementsMatch(t, getPrivilegeNames(groups[1].Privileges), []string{"priv20", "priv21"})
})
}
func getPrivilegeNames(privileges []*milvuspb.PrivilegeEntity) []string {
if len(privileges) == 0 {
return []string{}
}
return lo.Map(privileges, func(p *milvuspb.PrivilegeEntity, _ int) string {
return p.Name
})
}
func TestCatalog_AlterDatabase(t *testing.T) {

View File

@ -50,6 +50,9 @@ const (
// GranteeIDPrefix prefix for mapping among privilege and grantor
GranteeIDPrefix = ComponentPrefix + CommonCredentialPrefix + "/grantee-id"
// PrivilegeGroupPrefix prefix for privilege group
PrivilegeGroupPrefix = ComponentPrefix + "/privilege-group"
)
func BuildDatabasePrefixWithDBID(dbID int64) string {
@ -70,3 +73,7 @@ func getDatabasePrefix(dbID int64) string {
}
return CollectionMetaPrefix
}
func BuildPrivilegeGroupkey(groupName string) string {
return fmt.Sprintf("%s/%s", PrivilegeGroupPrefix, groupName)
}

View File

@ -1828,3 +1828,198 @@ func NewRootCoordCatalog(t interface {
return mock
}
// GetPrivilegeGroup provides a mock function with given fields: ctx, groupName
func (_m *RootCoordCatalog) GetPrivilegeGroup(ctx context.Context, groupName string) (*milvuspb.PrivilegeGroupInfo, error) {
ret := _m.Called(ctx, groupName)
var r0 *milvuspb.PrivilegeGroupInfo
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (*milvuspb.PrivilegeGroupInfo, error)); ok {
return rf(ctx, groupName)
}
if rf, ok := ret.Get(0).(func(context.Context, string) *milvuspb.PrivilegeGroupInfo); ok {
r0 = rf(ctx, groupName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*milvuspb.PrivilegeGroupInfo)
}
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, groupName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoordCatalog_GetPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivilegeGroup'
type RootCoordCatalog_GetPrivilegeGroup_Call struct {
*mock.Call
}
// GetPrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - groupName string
func (_e *RootCoordCatalog_Expecter) GetPrivilegeGroup(ctx interface{}, groupName interface{}) *RootCoordCatalog_GetPrivilegeGroup_Call {
return &RootCoordCatalog_GetPrivilegeGroup_Call{Call: _e.mock.On("GetPrivilegeGroup", ctx, groupName)}
}
func (_c *RootCoordCatalog_GetPrivilegeGroup_Call) Run(run func(ctx context.Context, groupName string)) *RootCoordCatalog_GetPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *RootCoordCatalog_GetPrivilegeGroup_Call) Return(_a0 *milvuspb.PrivilegeGroupInfo, _a1 error) *RootCoordCatalog_GetPrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoordCatalog_GetPrivilegeGroup_Call) RunAndReturn(run func(context.Context, string) (*milvuspb.PrivilegeGroupInfo,error)) *RootCoordCatalog_GetPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// DropPrivilegeGroup provides a mock function with given fields: ctx, groupName, privileges
func (_m *RootCoordCatalog) DropPrivilegeGroup(ctx context.Context, groupName string) error {
ret := _m.Called(ctx, groupName)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, groupName)
} else {
r0 = ret.Error(0)
}
return r0
}
// RootCoordCatalog_DropPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DropPrivilegeGroup'
type RootCoordCatalog_DropPrivilegeGroup_Call struct {
*mock.Call
}
// DropPrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - groupName string
func (_e *RootCoordCatalog_Expecter) DropPrivilegeGroup(ctx interface{}, groupName interface{}) *RootCoordCatalog_DropPrivilegeGroup_Call {
return &RootCoordCatalog_DropPrivilegeGroup_Call{Call: _e.mock.On("DropPrivilegeGroup", ctx, groupName)}
}
func (_c *RootCoordCatalog_DropPrivilegeGroup_Call) Run(run func(ctx context.Context, groupName string)) *RootCoordCatalog_DropPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *RootCoordCatalog_DropPrivilegeGroup_Call) Return(_a0 error) *RootCoordCatalog_DropPrivilegeGroup_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *RootCoordCatalog_DropPrivilegeGroup_Call) RunAndReturn(run func(context.Context, string) error) *RootCoordCatalog_DropPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// SavePrivilegeGroup provides a mock function with given fields: ctx, groupName, privileges
func (_m *RootCoordCatalog) SavePrivilegeGroup(ctx context.Context, data *milvuspb.PrivilegeGroupInfo) error {
ret := _m.Called(ctx, data)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.PrivilegeGroupInfo) error); ok {
r0 = rf(ctx, data)
} else {
r0 = ret.Error(0)
}
return r0
}
// RootCoordCatalog_SavePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SavePrivilegeGroup'
type RootCoordCatalog_SavePrivilegeGroup_Call struct {
*mock.Call
}
// SavePrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - groupName string
func (_e *RootCoordCatalog_Expecter) SavePrivilegeGroup(ctx interface{}, data interface{}) *RootCoordCatalog_SavePrivilegeGroup_Call {
return &RootCoordCatalog_SavePrivilegeGroup_Call{Call: _e.mock.On("SavePrivilegeGroup", ctx, data)}
}
func (_c *RootCoordCatalog_SavePrivilegeGroup_Call) Run(run func(ctx context.Context, data *milvuspb.PrivilegeGroupInfo)) *RootCoordCatalog_SavePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.PrivilegeGroupInfo))
})
return _c
}
func (_c *RootCoordCatalog_SavePrivilegeGroup_Call) Return(_a0 error) *RootCoordCatalog_SavePrivilegeGroup_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *RootCoordCatalog_SavePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.PrivilegeGroupInfo) error) *RootCoordCatalog_SavePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// ListPrivilegeGroups provides a mock function with given fields: ctx
func (_m *RootCoordCatalog) ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
ret := _m.Called(ctx)
var r0 []*milvuspb.PrivilegeGroupInfo
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) ([]*milvuspb.PrivilegeGroupInfo, error)); ok {
return rf(ctx)
}
if rf, ok := ret.Get(0).(func(context.Context) []*milvuspb.PrivilegeGroupInfo); ok {
r0 = rf(ctx)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*milvuspb.PrivilegeGroupInfo)
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(ctx)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoordCatalog_ListPrivilegeGroups_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrivilegeGroups'
type RootCoordCatalog_ListPrivilegeGroups_Call struct {
*mock.Call
}
// ListPrivilegeGroups is a helper method to define mock.On call
// - ctx context.Context
func (_e *RootCoordCatalog_Expecter) ListPrivilegeGroups(ctx interface{}) *RootCoordCatalog_ListPrivilegeGroups_Call {
return &RootCoordCatalog_ListPrivilegeGroups_Call{Call: _e.mock.On("ListPrivilegeGroups", ctx)}
}
func (_c *RootCoordCatalog_ListPrivilegeGroups_Call) Run(run func(ctx context.Context)) *RootCoordCatalog_ListPrivilegeGroups_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *RootCoordCatalog_ListPrivilegeGroups_Call) Return(_a0 []*milvuspb.PrivilegeGroupInfo, _a1 error) *RootCoordCatalog_ListPrivilegeGroups_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoordCatalog_ListPrivilegeGroups_Call) RunAndReturn(run func(context.Context) ([]*milvuspb.PrivilegeGroupInfo, error)) *RootCoordCatalog_ListPrivilegeGroups_Call {
_c.Call.Return(run)
return _c
}

View File

@ -211,6 +211,65 @@ func (_c *MockProxy_AlterCollection_Call) RunAndReturn(run func(context.Context,
return _c
}
// AlterCollectionField provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) AlterCollectionField(_a0 context.Context, _a1 *milvuspb.AlterCollectionFieldRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for AlterCollectionField")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.AlterCollectionFieldRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.AlterCollectionFieldRequest) *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.AlterCollectionFieldRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProxy_AlterCollectionField_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AlterCollectionField'
type MockProxy_AlterCollectionField_Call struct {
*mock.Call
}
// AlterCollectionField is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.AlterCollectionFieldRequest
func (_e *MockProxy_Expecter) AlterCollectionField(_a0 interface{}, _a1 interface{}) *MockProxy_AlterCollectionField_Call {
return &MockProxy_AlterCollectionField_Call{Call: _e.mock.On("AlterCollectionField", _a0, _a1)}
}
func (_c *MockProxy_AlterCollectionField_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.AlterCollectionFieldRequest)) *MockProxy_AlterCollectionField_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.AlterCollectionFieldRequest))
})
return _c
}
func (_c *MockProxy_AlterCollectionField_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_AlterCollectionField_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockProxy_AlterCollectionField_Call) RunAndReturn(run func(context.Context, *milvuspb.AlterCollectionFieldRequest) (*commonpb.Status, error)) *MockProxy_AlterCollectionField_Call {
_c.Call.Return(run)
return _c
}
// AlterDatabase provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) AlterDatabase(_a0 context.Context, _a1 *milvuspb.AlterDatabaseRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -919,6 +978,65 @@ func (_c *MockProxy_CreatePartition_Call) RunAndReturn(run func(context.Context,
return _c
}
// CreatePrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) CreatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for CreatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) *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.CreatePrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProxy_CreatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrivilegeGroup'
type MockProxy_CreatePrivilegeGroup_Call struct {
*mock.Call
}
// CreatePrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.CreatePrivilegeGroupRequest
func (_e *MockProxy_Expecter) CreatePrivilegeGroup(_a0 interface{}, _a1 interface{}) *MockProxy_CreatePrivilegeGroup_Call {
return &MockProxy_CreatePrivilegeGroup_Call{Call: _e.mock.On("CreatePrivilegeGroup", _a0, _a1)}
}
func (_c *MockProxy_CreatePrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest)) *MockProxy_CreatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.CreatePrivilegeGroupRequest))
})
return _c
}
func (_c *MockProxy_CreatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_CreatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockProxy_CreatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error)) *MockProxy_CreatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// CreateResourceGroup provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) CreateResourceGroup(_a0 context.Context, _a1 *milvuspb.CreateResourceGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -1804,6 +1922,65 @@ func (_c *MockProxy_DropPartition_Call) RunAndReturn(run func(context.Context, *
return _c
}
// DropPrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) DropPrivilegeGroup(_a0 context.Context, _a1 *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for DropPrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest) *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.DropPrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProxy_DropPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DropPrivilegeGroup'
type MockProxy_DropPrivilegeGroup_Call struct {
*mock.Call
}
// DropPrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.DropPrivilegeGroupRequest
func (_e *MockProxy_Expecter) DropPrivilegeGroup(_a0 interface{}, _a1 interface{}) *MockProxy_DropPrivilegeGroup_Call {
return &MockProxy_DropPrivilegeGroup_Call{Call: _e.mock.On("DropPrivilegeGroup", _a0, _a1)}
}
func (_c *MockProxy_DropPrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.DropPrivilegeGroupRequest)) *MockProxy_DropPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.DropPrivilegeGroupRequest))
})
return _c
}
func (_c *MockProxy_DropPrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_DropPrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockProxy_DropPrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error)) *MockProxy_DropPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// DropResourceGroup provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) DropResourceGroup(_a0 context.Context, _a1 *milvuspb.DropResourceGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -4488,6 +4665,65 @@ func (_c *MockProxy_ListIndexedSegment_Call) RunAndReturn(run func(context.Conte
return _c
}
// ListPrivilegeGroups provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) ListPrivilegeGroups(_a0 context.Context, _a1 *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for ListPrivilegeGroups")
}
var r0 *milvuspb.ListPrivilegeGroupsResponse
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) *milvuspb.ListPrivilegeGroupsResponse); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*milvuspb.ListPrivilegeGroupsResponse)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProxy_ListPrivilegeGroups_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrivilegeGroups'
type MockProxy_ListPrivilegeGroups_Call struct {
*mock.Call
}
// ListPrivilegeGroups is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.ListPrivilegeGroupsRequest
func (_e *MockProxy_Expecter) ListPrivilegeGroups(_a0 interface{}, _a1 interface{}) *MockProxy_ListPrivilegeGroups_Call {
return &MockProxy_ListPrivilegeGroups_Call{Call: _e.mock.On("ListPrivilegeGroups", _a0, _a1)}
}
func (_c *MockProxy_ListPrivilegeGroups_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.ListPrivilegeGroupsRequest)) *MockProxy_ListPrivilegeGroups_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.ListPrivilegeGroupsRequest))
})
return _c
}
func (_c *MockProxy_ListPrivilegeGroups_Call) Return(_a0 *milvuspb.ListPrivilegeGroupsResponse, _a1 error) *MockProxy_ListPrivilegeGroups_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockProxy_ListPrivilegeGroups_Call) RunAndReturn(run func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error)) *MockProxy_ListPrivilegeGroups_Call {
_c.Call.Return(run)
return _c
}
// ListResourceGroups provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) ListResourceGroups(_a0 context.Context, _a1 *milvuspb.ListResourceGroupsRequest) (*milvuspb.ListResourceGroupsResponse, error) {
ret := _m.Called(_a0, _a1)
@ -4842,6 +5078,65 @@ func (_c *MockProxy_OperatePrivilege_Call) RunAndReturn(run func(context.Context
return _c
}
// OperatePrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *MockProxy) OperatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OperatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) *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.OperatePrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockProxy_OperatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatePrivilegeGroup'
type MockProxy_OperatePrivilegeGroup_Call struct {
*mock.Call
}
// OperatePrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.OperatePrivilegeGroupRequest
func (_e *MockProxy_Expecter) OperatePrivilegeGroup(_a0 interface{}, _a1 interface{}) *MockProxy_OperatePrivilegeGroup_Call {
return &MockProxy_OperatePrivilegeGroup_Call{Call: _e.mock.On("OperatePrivilegeGroup", _a0, _a1)}
}
func (_c *MockProxy_OperatePrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeGroupRequest)) *MockProxy_OperatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.OperatePrivilegeGroupRequest))
})
return _c
}
func (_c *MockProxy_OperatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockProxy_OperatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockProxy_OperatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error)) *MockProxy_OperatePrivilegeGroup_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)

View File

@ -744,6 +744,65 @@ func (_c *RootCoord_CreatePartition_Call) RunAndReturn(run func(context.Context,
return _c
}
// CreatePrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) CreatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for CreatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) *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.CreatePrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoord_CreatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrivilegeGroup'
type RootCoord_CreatePrivilegeGroup_Call struct {
*mock.Call
}
// CreatePrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.CreatePrivilegeGroupRequest
func (_e *RootCoord_Expecter) CreatePrivilegeGroup(_a0 interface{}, _a1 interface{}) *RootCoord_CreatePrivilegeGroup_Call {
return &RootCoord_CreatePrivilegeGroup_Call{Call: _e.mock.On("CreatePrivilegeGroup", _a0, _a1)}
}
func (_c *RootCoord_CreatePrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.CreatePrivilegeGroupRequest)) *RootCoord_CreatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.CreatePrivilegeGroupRequest))
})
return _c
}
func (_c *RootCoord_CreatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *RootCoord_CreatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoord_CreatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error)) *RootCoord_CreatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// CreateRole provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) CreateRole(_a0 context.Context, _a1 *milvuspb.CreateRoleRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -1334,6 +1393,65 @@ func (_c *RootCoord_DropPartition_Call) RunAndReturn(run func(context.Context, *
return _c
}
// DropPrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) DropPrivilegeGroup(_a0 context.Context, _a1 *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for DropPrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest) *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.DropPrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoord_DropPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DropPrivilegeGroup'
type RootCoord_DropPrivilegeGroup_Call struct {
*mock.Call
}
// DropPrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.DropPrivilegeGroupRequest
func (_e *RootCoord_Expecter) DropPrivilegeGroup(_a0 interface{}, _a1 interface{}) *RootCoord_DropPrivilegeGroup_Call {
return &RootCoord_DropPrivilegeGroup_Call{Call: _e.mock.On("DropPrivilegeGroup", _a0, _a1)}
}
func (_c *RootCoord_DropPrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.DropPrivilegeGroupRequest)) *RootCoord_DropPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.DropPrivilegeGroupRequest))
})
return _c
}
func (_c *RootCoord_DropPrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *RootCoord_DropPrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoord_DropPrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error)) *RootCoord_DropPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// DropRole provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) DropRole(_a0 context.Context, _a1 *milvuspb.DropRoleRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -2205,6 +2323,65 @@ func (_c *RootCoord_ListPolicy_Call) RunAndReturn(run func(context.Context, *int
return _c
}
// ListPrivilegeGroups provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) ListPrivilegeGroups(_a0 context.Context, _a1 *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for ListPrivilegeGroups")
}
var r0 *milvuspb.ListPrivilegeGroupsResponse
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) *milvuspb.ListPrivilegeGroupsResponse); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*milvuspb.ListPrivilegeGroupsResponse)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoord_ListPrivilegeGroups_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrivilegeGroups'
type RootCoord_ListPrivilegeGroups_Call struct {
*mock.Call
}
// ListPrivilegeGroups is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.ListPrivilegeGroupsRequest
func (_e *RootCoord_Expecter) ListPrivilegeGroups(_a0 interface{}, _a1 interface{}) *RootCoord_ListPrivilegeGroups_Call {
return &RootCoord_ListPrivilegeGroups_Call{Call: _e.mock.On("ListPrivilegeGroups", _a0, _a1)}
}
func (_c *RootCoord_ListPrivilegeGroups_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.ListPrivilegeGroupsRequest)) *RootCoord_ListPrivilegeGroups_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.ListPrivilegeGroupsRequest))
})
return _c
}
func (_c *RootCoord_ListPrivilegeGroups_Call) Return(_a0 *milvuspb.ListPrivilegeGroupsResponse, _a1 error) *RootCoord_ListPrivilegeGroups_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoord_ListPrivilegeGroups_Call) RunAndReturn(run func(context.Context, *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error)) *RootCoord_ListPrivilegeGroups_Call {
_c.Call.Return(run)
return _c
}
// OperatePrivilege provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) OperatePrivilege(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
@ -2264,6 +2441,65 @@ func (_c *RootCoord_OperatePrivilege_Call) RunAndReturn(run func(context.Context
return _c
}
// OperatePrivilegeGroup provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) OperatePrivilegeGroup(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for OperatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) *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.OperatePrivilegeGroupRequest) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RootCoord_OperatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatePrivilegeGroup'
type RootCoord_OperatePrivilegeGroup_Call struct {
*mock.Call
}
// OperatePrivilegeGroup is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 *milvuspb.OperatePrivilegeGroupRequest
func (_e *RootCoord_Expecter) OperatePrivilegeGroup(_a0 interface{}, _a1 interface{}) *RootCoord_OperatePrivilegeGroup_Call {
return &RootCoord_OperatePrivilegeGroup_Call{Call: _e.mock.On("OperatePrivilegeGroup", _a0, _a1)}
}
func (_c *RootCoord_OperatePrivilegeGroup_Call) Run(run func(_a0 context.Context, _a1 *milvuspb.OperatePrivilegeGroupRequest)) *RootCoord_OperatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.OperatePrivilegeGroupRequest))
})
return _c
}
func (_c *RootCoord_OperatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *RootCoord_OperatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *RootCoord_OperatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error)) *RootCoord_OperatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// OperateUserRole provides a mock function with given fields: _a0, _a1
func (_m *RootCoord) OperateUserRole(_a0 context.Context, _a1 *milvuspb.OperateUserRoleRequest) (*commonpb.Status, error) {
ret := _m.Called(_a0, _a1)

View File

@ -966,6 +966,80 @@ func (_c *MockRootCoordClient_CreatePartition_Call) RunAndReturn(run func(contex
return _c
}
// CreatePrivilegeGroup provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for CreatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest, ...grpc.CallOption) *commonpb.Status); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*commonpb.Status)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockRootCoordClient_CreatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrivilegeGroup'
type MockRootCoordClient_CreatePrivilegeGroup_Call struct {
*mock.Call
}
// CreatePrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - in *milvuspb.CreatePrivilegeGroupRequest
// - opts ...grpc.CallOption
func (_e *MockRootCoordClient_Expecter) CreatePrivilegeGroup(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_CreatePrivilegeGroup_Call {
return &MockRootCoordClient_CreatePrivilegeGroup_Call{Call: _e.mock.On("CreatePrivilegeGroup",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *MockRootCoordClient_CreatePrivilegeGroup_Call) Run(run func(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption)) *MockRootCoordClient_CreatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
if a != nil {
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*milvuspb.CreatePrivilegeGroupRequest), variadicArgs...)
})
return _c
}
func (_c *MockRootCoordClient_CreatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockRootCoordClient_CreatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockRootCoordClient_CreatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)) *MockRootCoordClient_CreatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// CreateRole provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) CreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
@ -1706,6 +1780,80 @@ func (_c *MockRootCoordClient_DropPartition_Call) RunAndReturn(run func(context.
return _c
}
// DropPrivilegeGroup provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) DropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for DropPrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest, ...grpc.CallOption) *commonpb.Status); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*commonpb.Status)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockRootCoordClient_DropPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DropPrivilegeGroup'
type MockRootCoordClient_DropPrivilegeGroup_Call struct {
*mock.Call
}
// DropPrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - in *milvuspb.DropPrivilegeGroupRequest
// - opts ...grpc.CallOption
func (_e *MockRootCoordClient_Expecter) DropPrivilegeGroup(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_DropPrivilegeGroup_Call {
return &MockRootCoordClient_DropPrivilegeGroup_Call{Call: _e.mock.On("DropPrivilegeGroup",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *MockRootCoordClient_DropPrivilegeGroup_Call) Run(run func(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption)) *MockRootCoordClient_DropPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
if a != nil {
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*milvuspb.DropPrivilegeGroupRequest), variadicArgs...)
})
return _c
}
func (_c *MockRootCoordClient_DropPrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockRootCoordClient_DropPrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockRootCoordClient_DropPrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.DropPrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)) *MockRootCoordClient_DropPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// DropRole provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) DropRole(ctx context.Context, in *milvuspb.DropRoleRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
@ -2742,6 +2890,80 @@ func (_c *MockRootCoordClient_ListPolicy_Call) RunAndReturn(run func(context.Con
return _c
}
// ListPrivilegeGroups provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) ListPrivilegeGroups(ctx context.Context, in *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for ListPrivilegeGroups")
}
var r0 *milvuspb.ListPrivilegeGroupsResponse
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest, ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest, ...grpc.CallOption) *milvuspb.ListPrivilegeGroupsResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*milvuspb.ListPrivilegeGroupsResponse)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.ListPrivilegeGroupsRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockRootCoordClient_ListPrivilegeGroups_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrivilegeGroups'
type MockRootCoordClient_ListPrivilegeGroups_Call struct {
*mock.Call
}
// ListPrivilegeGroups is a helper method to define mock.On call
// - ctx context.Context
// - in *milvuspb.ListPrivilegeGroupsRequest
// - opts ...grpc.CallOption
func (_e *MockRootCoordClient_Expecter) ListPrivilegeGroups(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_ListPrivilegeGroups_Call {
return &MockRootCoordClient_ListPrivilegeGroups_Call{Call: _e.mock.On("ListPrivilegeGroups",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *MockRootCoordClient_ListPrivilegeGroups_Call) Run(run func(ctx context.Context, in *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption)) *MockRootCoordClient_ListPrivilegeGroups_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
if a != nil {
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*milvuspb.ListPrivilegeGroupsRequest), variadicArgs...)
})
return _c
}
func (_c *MockRootCoordClient_ListPrivilegeGroups_Call) Return(_a0 *milvuspb.ListPrivilegeGroupsResponse, _a1 error) *MockRootCoordClient_ListPrivilegeGroups_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockRootCoordClient_ListPrivilegeGroups_Call) RunAndReturn(run func(context.Context, *milvuspb.ListPrivilegeGroupsRequest, ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error)) *MockRootCoordClient_ListPrivilegeGroups_Call {
_c.Call.Return(run)
return _c
}
// OperatePrivilege provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivilegeRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
@ -2816,6 +3038,80 @@ func (_c *MockRootCoordClient_OperatePrivilege_Call) RunAndReturn(run func(conte
return _c
}
// OperatePrivilegeGroup provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for OperatePrivilegeGroup")
}
var r0 *commonpb.Status
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)); ok {
return rf(ctx, in, opts...)
}
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest, ...grpc.CallOption) *commonpb.Status); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*commonpb.Status)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest, ...grpc.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MockRootCoordClient_OperatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatePrivilegeGroup'
type MockRootCoordClient_OperatePrivilegeGroup_Call struct {
*mock.Call
}
// OperatePrivilegeGroup is a helper method to define mock.On call
// - ctx context.Context
// - in *milvuspb.OperatePrivilegeGroupRequest
// - opts ...grpc.CallOption
func (_e *MockRootCoordClient_Expecter) OperatePrivilegeGroup(ctx interface{}, in interface{}, opts ...interface{}) *MockRootCoordClient_OperatePrivilegeGroup_Call {
return &MockRootCoordClient_OperatePrivilegeGroup_Call{Call: _e.mock.On("OperatePrivilegeGroup",
append([]interface{}{ctx, in}, opts...)...)}
}
func (_c *MockRootCoordClient_OperatePrivilegeGroup_Call) Run(run func(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption)) *MockRootCoordClient_OperatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]grpc.CallOption, len(args)-2)
for i, a := range args[2:] {
if a != nil {
variadicArgs[i] = a.(grpc.CallOption)
}
}
run(args[0].(context.Context), args[1].(*milvuspb.OperatePrivilegeGroupRequest), variadicArgs...)
})
return _c
}
func (_c *MockRootCoordClient_OperatePrivilegeGroup_Call) Return(_a0 *commonpb.Status, _a1 error) *MockRootCoordClient_OperatePrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *MockRootCoordClient_OperatePrivilegeGroup_Call) RunAndReturn(run func(context.Context, *milvuspb.OperatePrivilegeGroupRequest, ...grpc.CallOption) (*commonpb.Status, error)) *MockRootCoordClient_OperatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// OperateUserRole provides a mock function with given fields: ctx, in, opts
func (_m *MockRootCoordClient) OperateUserRole(ctx context.Context, in *milvuspb.OperateUserRoleRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
_va := make([]interface{}, len(opts))

View File

@ -4,6 +4,7 @@ option go_package = "github.com/milvus-io/milvus/internal/proto/internalpb";
import "common.proto";
import "schema.proto";
import "milvus.proto";
message GetTimeTickChannelRequest {
}
@ -263,6 +264,7 @@ message ListPolicyResponse {
common.Status status = 1;
repeated string policy_infos = 2;
repeated string user_roles = 3;
repeated milvus.PrivilegeGroupInfo privilege_groups = 4;
}
message ShowConfigurationsRequest {

View File

@ -126,6 +126,10 @@ service RootCoord {
rpc ListPolicy(internal.ListPolicyRequest) returns (internal.ListPolicyResponse) {}
rpc BackupRBAC(milvus.BackupRBACMetaRequest) returns (milvus.BackupRBACMetaResponse){}
rpc RestoreRBAC(milvus.RestoreRBACMetaRequest) returns (common.Status){}
rpc CreatePrivilegeGroup(milvus.CreatePrivilegeGroupRequest) returns (common.Status) {}
rpc DropPrivilegeGroup(milvus.DropPrivilegeGroupRequest) returns (common.Status) {}
rpc ListPrivilegeGroups(milvus.ListPrivilegeGroupsRequest) returns (milvus.ListPrivilegeGroupsResponse) {}
rpc OperatePrivilegeGroup(milvus.OperatePrivilegeGroupRequest) returns (common.Status) {}
rpc CheckHealth(milvus.CheckHealthRequest) returns (milvus.CheckHealthResponse) {}

View File

@ -6506,3 +6506,124 @@ func (node *Proxy) RegisterRestRouter(router gin.IRouter) {
router.GET(http.DNSegmentsPath, getDataComponentMetrics(node, metricsinfo.DataSegments))
router.GET(http.DNChannelsPath, getDataComponentMetrics(node, metricsinfo.DataChannels))
}
func (node *Proxy) CreatePrivilegeGroup(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-CreatePrivilegeGroup")
defer sp.End()
log := log.Ctx(ctx)
log.Info("CreatePrivilegeGroup", zap.Any("req", req))
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 req.Base == nil {
req.Base = &commonpb.MsgBase{}
}
req.Base.MsgType = commonpb.MsgType_CreatePrivilegeGroup
result, err := node.rootCoord.CreatePrivilegeGroup(ctx, req)
if err != nil {
log.Warn("fail to create privilege group", zap.Error(err))
return merr.Status(err), nil
}
if merr.Ok(result) {
SendReplicateMessagePack(ctx, node.replicateMsgStream, req)
}
return result, nil
}
func (node *Proxy) DropPrivilegeGroup(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-DropPrivilegeGroup")
defer sp.End()
log := log.Ctx(ctx)
log.Info("DropPrivilegeGroup", zap.Any("req", req))
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 req.Base == nil {
req.Base = &commonpb.MsgBase{}
}
req.Base.MsgType = commonpb.MsgType_DropPrivilegeGroup
result, err := node.rootCoord.DropPrivilegeGroup(ctx, req)
if err != nil {
log.Warn("fail to drop privilege group", zap.Error(err))
return merr.Status(err), nil
}
if merr.Ok(result) {
SendReplicateMessagePack(ctx, node.replicateMsgStream, req)
}
return result, nil
}
func (node *Proxy) ListPrivilegeGroups(ctx context.Context, req *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-ListPrivilegeGroups")
defer sp.End()
log := log.Ctx(ctx).With(
zap.String("role", typeutil.ProxyRole))
log.Debug("ListPrivilegeGroups")
if err := merr.CheckHealthy(node.GetStateCode()); err != nil {
return &milvuspb.ListPrivilegeGroupsResponse{Status: merr.Status(err)}, nil
}
if req.Base == nil {
req.Base = &commonpb.MsgBase{}
}
req.Base.MsgType = commonpb.MsgType_ListPrivilegeGroups
rootCoordReq := &milvuspb.ListPrivilegeGroupsRequest{
Base: commonpbutil.NewMsgBase(
commonpbutil.WithMsgType(commonpb.MsgType_ListPrivilegeGroups),
),
}
resp, err := node.rootCoord.ListPrivilegeGroups(ctx, rootCoordReq)
if err != nil {
return &milvuspb.ListPrivilegeGroupsResponse{
Status: merr.Status(err),
}, nil
}
return resp, nil
}
func (node *Proxy) OperatePrivilegeGroup(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
ctx, sp := otel.Tracer(typeutil.ProxyRole).Start(ctx, "Proxy-OperatePrivilegeGroup")
defer sp.End()
log := log.Ctx(ctx)
log.Info("OperatePrivilegeGroup", zap.Any("req", req))
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
}
for _, priv := range req.GetPrivileges() {
if err := ValidatePrivilege(priv.Name); err != nil {
return merr.Status(err), nil
}
}
if req.Base == nil {
req.Base = &commonpb.MsgBase{}
}
req.Base.MsgType = commonpb.MsgType_OperatePrivilegeGroup
result, err := node.rootCoord.OperatePrivilegeGroup(ctx, req)
if err != nil {
log.Warn("fail to operate privilege group", zap.Error(err))
return merr.Status(err), nil
}
if merr.Ok(result) {
SendReplicateMessagePack(ctx, node.replicateMsgStream, req)
}
return result, nil
}

View File

@ -1123,9 +1123,15 @@ func (m *MetaCache) RefreshPolicyInfo(op typeutil.CacheOp) (err error) {
switch op.OpType {
case typeutil.CacheGrantPrivilege:
m.privilegeInfos[op.OpKey] = struct{}{}
keys := funcutil.PrivilegesForPolicy(op.OpKey)
for _, key := range keys {
m.privilegeInfos[key] = struct{}{}
}
case typeutil.CacheRevokePrivilege:
delete(m.privilegeInfos, op.OpKey)
keys := funcutil.PrivilegesForPolicy(op.OpKey)
for _, key := range keys {
delete(m.privilegeInfos, key)
}
case typeutil.CacheAddUserToRole:
user, role, err := funcutil.DecodeUserRoleCache(op.OpKey)
if err != nil {

View File

@ -1134,6 +1134,22 @@ func (coord *RootCoordMock) RestoreRBAC(ctx context.Context, in *milvuspb.Restor
return &commonpb.Status{}, nil
}
func (coord *RootCoordMock) CreatePrivilegeGroup(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, nil
}
func (coord *RootCoordMock) DropPrivilegeGroup(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, nil
}
func (coord *RootCoordMock) ListPrivilegeGroups(ctx context.Context, req *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error) {
return &milvuspb.ListPrivilegeGroupsResponse{}, nil
}
func (coord *RootCoordMock) OperatePrivilegeGroup(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, nil
}
type DescribeCollectionFunc func(ctx context.Context, request *milvuspb.DescribeCollectionRequest, opts ...grpc.CallOption) (*milvuspb.DescribeCollectionResponse, error)
type ShowPartitionsFunc func(ctx context.Context, request *milvuspb.ShowPartitionsRequest, opts ...grpc.CallOption) (*milvuspb.ShowPartitionsResponse, error)

View File

@ -1071,14 +1071,6 @@ func ValidateObjectType(entity string) error {
return validateName(entity, "ObjectType")
}
func ValidatePrincipalName(entity string) error {
return validateName(entity, "PrincipalName")
}
func ValidatePrincipalType(entity string) error {
return validateName(entity, "PrincipalType")
}
func ValidatePrivilege(entity string) error {
if util.IsAnyWord(entity) {
return nil

View File

@ -804,8 +804,6 @@ func TestValidateName(t *testing.T) {
assert.Nil(t, ValidateRoleName(name))
assert.Nil(t, ValidateObjectName(name))
assert.Nil(t, ValidateObjectType(name))
assert.Nil(t, ValidatePrincipalName(name))
assert.Nil(t, ValidatePrincipalType(name))
assert.Nil(t, ValidatePrivilege(name))
}
@ -828,8 +826,6 @@ func TestValidateName(t *testing.T) {
assert.NotNil(t, validateName(name, nameType))
assert.NotNil(t, ValidateRoleName(name))
assert.NotNil(t, ValidateObjectType(name))
assert.NotNil(t, ValidatePrincipalName(name))
assert.NotNil(t, ValidatePrincipalType(name))
assert.NotNil(t, ValidatePrivilege(name))
}
assert.NotNil(t, ValidateObjectName(" "))

View File

@ -365,6 +365,10 @@ func (_c *MockScheduler_GetSegmentTaskNum_Call) RunAndReturn(run func() int) *Mo
func (_m *MockScheduler) GetTasksJSON() string {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetTasksJSON")
}
var r0 string
if rf, ok := ret.Get(0).(func() string); ok {
r0 = rf()

View File

@ -99,6 +99,12 @@ type IMetaTable interface {
ListUserRole(tenant string) ([]string, error)
BackupRBAC(ctx context.Context, tenant string) (*milvuspb.RBACMeta, error)
RestoreRBAC(ctx context.Context, tenant string, meta *milvuspb.RBACMeta) error
IsCustomPrivilegeGroup(groupName string) (bool, error)
CreatePrivilegeGroup(groupName string) error
DropPrivilegeGroup(groupName string) error
ListPrivilegeGroups() ([]*milvuspb.PrivilegeGroupInfo, error)
OperatePrivilegeGroup(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error
GetPrivilegeGroupRoles(groupName string) ([]*milvuspb.RoleEntity, error)
}
// MetaTable is a persistent meta set of all databases, collections and partitions.
@ -1456,3 +1462,187 @@ func (mt *MetaTable) RestoreRBAC(ctx context.Context, tenant string, meta *milvu
return mt.catalog.RestoreRBAC(mt.ctx, tenant, meta)
}
// check if the privielge group name is defined by users
func (mt *MetaTable) IsCustomPrivilegeGroup(groupName string) (bool, error) {
privGroups, err := mt.catalog.ListPrivilegeGroups(mt.ctx)
if err != nil {
return false, err
}
for _, group := range privGroups {
if group.GroupName == groupName {
return true, nil
}
}
return false, nil
}
func (mt *MetaTable) CreatePrivilegeGroup(groupName string) error {
if funcutil.IsEmptyString(groupName) {
return fmt.Errorf("the privilege group name is empty")
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(groupName)
if err != nil {
return err
}
if definedByUsers {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by users", groupName)
}
if util.IsPrivilegeNameDefined(groupName) {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by built in privileges or privilege groups in system", groupName)
}
data := &milvuspb.PrivilegeGroupInfo{
GroupName: groupName,
Privileges: make([]*milvuspb.PrivilegeEntity, 0),
}
return mt.catalog.SavePrivilegeGroup(mt.ctx, data)
}
func (mt *MetaTable) DropPrivilegeGroup(groupName string) error {
if funcutil.IsEmptyString(groupName) {
return fmt.Errorf("the privilege group name is empty")
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(groupName)
if err != nil {
return err
}
if !definedByUsers {
return nil
}
// check if the group is used by any role
roles, err := mt.catalog.ListRole(mt.ctx, util.DefaultTenant, nil, false)
if err != nil {
return err
}
roleEntity := lo.Map(roles, func(entity *milvuspb.RoleResult, _ int) *milvuspb.RoleEntity {
return entity.GetRole()
})
for _, role := range roleEntity {
grants, err := mt.catalog.ListGrant(mt.ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: role,
DbName: util.AnyWord,
})
if err != nil {
return err
}
for _, grant := range grants {
if grant.Grantor.Privilege.Name == groupName {
return errors.Newf("privilege group [%s] is used by role [%s], Use REVOKE API to revoke it first", groupName, role.GetName())
}
}
}
return mt.catalog.DropPrivilegeGroup(mt.ctx, groupName)
}
func (mt *MetaTable) ListPrivilegeGroups() ([]*milvuspb.PrivilegeGroupInfo, error) {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
return mt.catalog.ListPrivilegeGroups(mt.ctx)
}
func (mt *MetaTable) OperatePrivilegeGroup(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
if funcutil.IsEmptyString(groupName) {
return fmt.Errorf("the privilege group name is empty")
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
// validate input params
definedByUsers, err := mt.IsCustomPrivilegeGroup(groupName)
if err != nil {
return err
}
if !definedByUsers {
return merr.WrapErrParameterInvalidMsg("there is no privilege group name [%s] to operate", groupName)
}
groups, err := mt.catalog.ListPrivilegeGroups(mt.ctx)
if err != nil {
return err
}
for _, p := range privileges {
if util.IsPrivilegeNameDefined(p.Name) {
continue
}
for _, group := range groups {
// add privileges for custom privilege group
if group.GroupName == p.Name {
privileges = append(privileges, group.Privileges...)
} else {
return merr.WrapErrParameterInvalidMsg("there is no privilege name or privielge group name [%s] defined in system to operate", p.Name)
}
}
}
// merge with current privileges
group, err := mt.catalog.GetPrivilegeGroup(mt.ctx, groupName)
if err != nil {
log.Warn("fail to get privilege group", zap.String("privilege_group", groupName), zap.Error(err))
return err
}
privSet := lo.SliceToMap(group.Privileges, func(p *milvuspb.PrivilegeEntity) (string, struct{}) {
return p.Name, struct{}{}
})
switch operateType {
case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup:
for _, p := range privileges {
privSet[p.Name] = struct{}{}
}
case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup:
for _, p := range privileges {
delete(privSet, p.Name)
}
default:
log.Warn("unsupported operate type", zap.Any("operate_type", operateType))
return fmt.Errorf("unsupported operate type: %v", operateType)
}
mergedPrivs := lo.Map(lo.Keys(privSet), func(priv string, _ int) *milvuspb.PrivilegeEntity {
return &milvuspb.PrivilegeEntity{Name: priv}
})
data := &milvuspb.PrivilegeGroupInfo{
GroupName: groupName,
Privileges: mergedPrivs,
}
return mt.catalog.SavePrivilegeGroup(mt.ctx, data)
}
func (mt *MetaTable) GetPrivilegeGroupRoles(groupName string) ([]*milvuspb.RoleEntity, error) {
if funcutil.IsEmptyString(groupName) {
return nil, fmt.Errorf("the privilege group name is empty")
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
// get all roles
roles, err := mt.catalog.ListRole(mt.ctx, util.DefaultTenant, nil, false)
if err != nil {
return nil, err
}
roleEntity := lo.Map(roles, func(entity *milvuspb.RoleResult, _ int) *milvuspb.RoleEntity {
return entity.GetRole()
})
rolesMap := make(map[*milvuspb.RoleEntity]struct{})
for _, role := range roleEntity {
grants, err := mt.catalog.ListGrant(mt.ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: role,
DbName: util.AnyWord,
})
if err != nil {
return nil, err
}
for _, grant := range grants {
if grant.Grantor.Privilege.Name == groupName {
rolesMap[role] = struct{}{}
}
}
}
return lo.Keys(rolesMap), nil
}

View File

@ -2071,3 +2071,32 @@ func TestMetaTable_RestoreRBAC(t *testing.T) {
err = mt.RestoreRBAC(context.TODO(), util.DefaultTenant, &milvuspb.RBACMeta{})
assert.Error(t, err)
}
func TestMetaTable_PrivilegeGroup(t *testing.T) {
catalog := mocks.NewRootCoordCatalog(t)
catalog.EXPECT().ListPrivilegeGroups(mock.Anything).Return([]*milvuspb.PrivilegeGroupInfo{
{
GroupName: "pg1",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}, {Name: "DescribeCollection"}},
},
}, nil)
catalog.EXPECT().ListRole(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
catalog.EXPECT().SavePrivilegeGroup(mock.Anything, mock.Anything).Return(nil)
catalog.EXPECT().DropPrivilegeGroup(mock.Anything, mock.Anything).Return(nil)
mt := &MetaTable{
dbName2Meta: map[string]*model.Database{
"not_commit": model.NewDatabase(1, "not_commit", pb.DatabaseState_DatabaseCreated, nil),
},
names: newNameDb(),
aliases: newNameDb(),
catalog: catalog,
}
err := mt.CreatePrivilegeGroup("pg1")
assert.Error(t, err)
err = mt.CreatePrivilegeGroup("pg2")
assert.NoError(t, err)
err = mt.DropPrivilegeGroup("pg1")
assert.NoError(t, err)
_, err = mt.ListPrivilegeGroups()
assert.NoError(t, err)
}

View File

@ -97,6 +97,10 @@ type mockMetaTable struct {
ListPolicyFunc func(tenant string) ([]string, error)
ListUserRoleFunc func(tenant string) ([]string, error)
DescribeDatabaseFunc func(ctx context.Context, dbName string) (*model.Database, error)
CreatePrivilegeGroupFunc func(groupName string) error
DropPrivilegeGroupFunc func(groupName string) error
ListPrivilegeGroupsFunc func() ([]*milvuspb.PrivilegeGroupInfo, error)
OperatePrivilegeGroupFunc func(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error
}
func (m mockMetaTable) GetDatabaseByName(ctx context.Context, dbName string, ts Timestamp) (*model.Database, error) {
@ -251,6 +255,22 @@ func (m mockMetaTable) ListUserRole(tenant string) ([]string, error) {
return m.ListUserRoleFunc(tenant)
}
func (m mockMetaTable) CreatePrivilegeGroup(groupName string) error {
return m.CreatePrivilegeGroupFunc(groupName)
}
func (m mockMetaTable) DropPrivilegeGroup(groupName string) error {
return m.DropPrivilegeGroupFunc(groupName)
}
func (m mockMetaTable) ListPrivilegeGroups() ([]*milvuspb.PrivilegeGroupInfo, error) {
return m.ListPrivilegeGroupsFunc()
}
func (m mockMetaTable) OperatePrivilegeGroup(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
return m.OperatePrivilegeGroupFunc(groupName, privileges, operateType)
}
func newMockMetaTable() *mockMetaTable {
return &mockMetaTable{}
}
@ -527,6 +547,18 @@ func withInvalidMeta() Opt {
meta.DescribeDatabaseFunc = func(ctx context.Context, dbName string) (*model.Database, error) {
return nil, errors.New("error mock DescribeDatabase")
}
meta.CreatePrivilegeGroupFunc = func(groupName string) error {
return errors.New("error mock CreatePrivilegeGroup")
}
meta.DropPrivilegeGroupFunc = func(groupName string) error {
return errors.New("error mock DropPrivilegeGroup")
}
meta.ListPrivilegeGroupsFunc = func() ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, errors.New("error mock ListPrivilegeGroups")
}
meta.OperatePrivilegeGroupFunc = func(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
return errors.New("error mock OperatePrivilegeGroup")
}
return withMeta(meta)
}

View File

@ -620,6 +620,52 @@ func (_c *IMetaTable_CreateDatabase_Call) RunAndReturn(run func(context.Context,
return _c
}
// CreatePrivilegeGroup provides a mock function with given fields: groupName
func (_m *IMetaTable) CreatePrivilegeGroup(groupName string) error {
ret := _m.Called(groupName)
if len(ret) == 0 {
panic("no return value specified for CreatePrivilegeGroup")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(groupName)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CreatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreatePrivilegeGroup'
type IMetaTable_CreatePrivilegeGroup_Call struct {
*mock.Call
}
// CreatePrivilegeGroup is a helper method to define mock.On call
// - groupName string
func (_e *IMetaTable_Expecter) CreatePrivilegeGroup(groupName interface{}) *IMetaTable_CreatePrivilegeGroup_Call {
return &IMetaTable_CreatePrivilegeGroup_Call{Call: _e.mock.On("CreatePrivilegeGroup", groupName)}
}
func (_c *IMetaTable_CreatePrivilegeGroup_Call) Run(run func(groupName string)) *IMetaTable_CreatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *IMetaTable_CreatePrivilegeGroup_Call) Return(_a0 error) *IMetaTable_CreatePrivilegeGroup_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CreatePrivilegeGroup_Call) RunAndReturn(run func(string) error) *IMetaTable_CreatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// CreateRole provides a mock function with given fields: tenant, entity
func (_m *IMetaTable) CreateRole(tenant string, entity *milvuspb.RoleEntity) error {
ret := _m.Called(tenant, entity)
@ -916,6 +962,52 @@ func (_c *IMetaTable_DropGrant_Call) RunAndReturn(run func(string, *milvuspb.Rol
return _c
}
// DropPrivilegeGroup provides a mock function with given fields: groupName
func (_m *IMetaTable) DropPrivilegeGroup(groupName string) error {
ret := _m.Called(groupName)
if len(ret) == 0 {
panic("no return value specified for DropPrivilegeGroup")
}
var r0 error
if rf, ok := ret.Get(0).(func(string) error); ok {
r0 = rf(groupName)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_DropPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DropPrivilegeGroup'
type IMetaTable_DropPrivilegeGroup_Call struct {
*mock.Call
}
// DropPrivilegeGroup is a helper method to define mock.On call
// - groupName string
func (_e *IMetaTable_Expecter) DropPrivilegeGroup(groupName interface{}) *IMetaTable_DropPrivilegeGroup_Call {
return &IMetaTable_DropPrivilegeGroup_Call{Call: _e.mock.On("DropPrivilegeGroup", groupName)}
}
func (_c *IMetaTable_DropPrivilegeGroup_Call) Run(run func(groupName string)) *IMetaTable_DropPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *IMetaTable_DropPrivilegeGroup_Call) Return(_a0 error) *IMetaTable_DropPrivilegeGroup_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_DropPrivilegeGroup_Call) RunAndReturn(run func(string) error) *IMetaTable_DropPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// DropRole provides a mock function with given fields: tenant, roleName
func (_m *IMetaTable) DropRole(tenant string, roleName string) error {
ret := _m.Called(tenant, roleName)
@ -1419,6 +1511,64 @@ func (_c *IMetaTable_GetPChannelInfo_Call) RunAndReturn(run func(string) *rootco
return _c
}
// GetPrivilegeGroupRoles provides a mock function with given fields: groupName
func (_m *IMetaTable) GetPrivilegeGroupRoles(groupName string) ([]*milvuspb.RoleEntity, error) {
ret := _m.Called(groupName)
if len(ret) == 0 {
panic("no return value specified for GetPrivilegeGroupRoles")
}
var r0 []*milvuspb.RoleEntity
var r1 error
if rf, ok := ret.Get(0).(func(string) ([]*milvuspb.RoleEntity, error)); ok {
return rf(groupName)
}
if rf, ok := ret.Get(0).(func(string) []*milvuspb.RoleEntity); ok {
r0 = rf(groupName)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*milvuspb.RoleEntity)
}
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(groupName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// IMetaTable_GetPrivilegeGroupRoles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetPrivilegeGroupRoles'
type IMetaTable_GetPrivilegeGroupRoles_Call struct {
*mock.Call
}
// GetPrivilegeGroupRoles is a helper method to define mock.On call
// - groupName string
func (_e *IMetaTable_Expecter) GetPrivilegeGroupRoles(groupName interface{}) *IMetaTable_GetPrivilegeGroupRoles_Call {
return &IMetaTable_GetPrivilegeGroupRoles_Call{Call: _e.mock.On("GetPrivilegeGroupRoles", groupName)}
}
func (_c *IMetaTable_GetPrivilegeGroupRoles_Call) Run(run func(groupName string)) *IMetaTable_GetPrivilegeGroupRoles_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *IMetaTable_GetPrivilegeGroupRoles_Call) Return(_a0 []*milvuspb.RoleEntity, _a1 error) *IMetaTable_GetPrivilegeGroupRoles_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *IMetaTable_GetPrivilegeGroupRoles_Call) RunAndReturn(run func(string) ([]*milvuspb.RoleEntity, error)) *IMetaTable_GetPrivilegeGroupRoles_Call {
_c.Call.Return(run)
return _c
}
// IsAlias provides a mock function with given fields: db, name
func (_m *IMetaTable) IsAlias(db string, name string) bool {
ret := _m.Called(db, name)
@ -1466,6 +1616,62 @@ func (_c *IMetaTable_IsAlias_Call) RunAndReturn(run func(string, string) bool) *
return _c
}
// IsCustomPrivilegeGroup provides a mock function with given fields: groupName
func (_m *IMetaTable) IsCustomPrivilegeGroup(groupName string) (bool, error) {
ret := _m.Called(groupName)
if len(ret) == 0 {
panic("no return value specified for IsCustomPrivilegeGroup")
}
var r0 bool
var r1 error
if rf, ok := ret.Get(0).(func(string) (bool, error)); ok {
return rf(groupName)
}
if rf, ok := ret.Get(0).(func(string) bool); ok {
r0 = rf(groupName)
} else {
r0 = ret.Get(0).(bool)
}
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(groupName)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// IMetaTable_IsCustomPrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IsCustomPrivilegeGroup'
type IMetaTable_IsCustomPrivilegeGroup_Call struct {
*mock.Call
}
// IsCustomPrivilegeGroup is a helper method to define mock.On call
// - groupName string
func (_e *IMetaTable_Expecter) IsCustomPrivilegeGroup(groupName interface{}) *IMetaTable_IsCustomPrivilegeGroup_Call {
return &IMetaTable_IsCustomPrivilegeGroup_Call{Call: _e.mock.On("IsCustomPrivilegeGroup", groupName)}
}
func (_c *IMetaTable_IsCustomPrivilegeGroup_Call) Run(run func(groupName string)) *IMetaTable_IsCustomPrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string))
})
return _c
}
func (_c *IMetaTable_IsCustomPrivilegeGroup_Call) Return(_a0 bool, _a1 error) *IMetaTable_IsCustomPrivilegeGroup_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *IMetaTable_IsCustomPrivilegeGroup_Call) RunAndReturn(run func(string) (bool, error)) *IMetaTable_IsCustomPrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// ListAliases provides a mock function with given fields: ctx, dbName, collectionName, ts
func (_m *IMetaTable) ListAliases(ctx context.Context, dbName string, collectionName string, ts uint64) ([]string, error) {
ret := _m.Called(ctx, dbName, collectionName, ts)
@ -1905,6 +2111,63 @@ func (_c *IMetaTable_ListPolicy_Call) RunAndReturn(run func(string) ([]string, e
return _c
}
// ListPrivilegeGroups provides a mock function with given fields:
func (_m *IMetaTable) ListPrivilegeGroups() ([]*milvuspb.PrivilegeGroupInfo, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for ListPrivilegeGroups")
}
var r0 []*milvuspb.PrivilegeGroupInfo
var r1 error
if rf, ok := ret.Get(0).(func() ([]*milvuspb.PrivilegeGroupInfo, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() []*milvuspb.PrivilegeGroupInfo); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*milvuspb.PrivilegeGroupInfo)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// IMetaTable_ListPrivilegeGroups_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPrivilegeGroups'
type IMetaTable_ListPrivilegeGroups_Call struct {
*mock.Call
}
// ListPrivilegeGroups is a helper method to define mock.On call
func (_e *IMetaTable_Expecter) ListPrivilegeGroups() *IMetaTable_ListPrivilegeGroups_Call {
return &IMetaTable_ListPrivilegeGroups_Call{Call: _e.mock.On("ListPrivilegeGroups")}
}
func (_c *IMetaTable_ListPrivilegeGroups_Call) Run(run func()) *IMetaTable_ListPrivilegeGroups_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *IMetaTable_ListPrivilegeGroups_Call) Return(_a0 []*milvuspb.PrivilegeGroupInfo, _a1 error) *IMetaTable_ListPrivilegeGroups_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *IMetaTable_ListPrivilegeGroups_Call) RunAndReturn(run func() ([]*milvuspb.PrivilegeGroupInfo, error)) *IMetaTable_ListPrivilegeGroups_Call {
_c.Call.Return(run)
return _c
}
// ListUserRole provides a mock function with given fields: tenant
func (_m *IMetaTable) ListUserRole(tenant string) ([]string, error) {
ret := _m.Called(tenant)
@ -2011,6 +2274,54 @@ func (_c *IMetaTable_OperatePrivilege_Call) RunAndReturn(run func(string, *milvu
return _c
}
// OperatePrivilegeGroup provides a mock function with given fields: groupName, privileges, operateType
func (_m *IMetaTable) OperatePrivilegeGroup(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
ret := _m.Called(groupName, privileges, operateType)
if len(ret) == 0 {
panic("no return value specified for OperatePrivilegeGroup")
}
var r0 error
if rf, ok := ret.Get(0).(func(string, []*milvuspb.PrivilegeEntity, milvuspb.OperatePrivilegeGroupType) error); ok {
r0 = rf(groupName, privileges, operateType)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_OperatePrivilegeGroup_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OperatePrivilegeGroup'
type IMetaTable_OperatePrivilegeGroup_Call struct {
*mock.Call
}
// OperatePrivilegeGroup is a helper method to define mock.On call
// - groupName string
// - privileges []*milvuspb.PrivilegeEntity
// - operateType milvuspb.OperatePrivilegeGroupType
func (_e *IMetaTable_Expecter) OperatePrivilegeGroup(groupName interface{}, privileges interface{}, operateType interface{}) *IMetaTable_OperatePrivilegeGroup_Call {
return &IMetaTable_OperatePrivilegeGroup_Call{Call: _e.mock.On("OperatePrivilegeGroup", groupName, privileges, operateType)}
}
func (_c *IMetaTable_OperatePrivilegeGroup_Call) Run(run func(groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType)) *IMetaTable_OperatePrivilegeGroup_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(string), args[1].([]*milvuspb.PrivilegeEntity), args[2].(milvuspb.OperatePrivilegeGroupType))
})
return _c
}
func (_c *IMetaTable_OperatePrivilegeGroup_Call) Return(_a0 error) *IMetaTable_OperatePrivilegeGroup_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_OperatePrivilegeGroup_Call) RunAndReturn(run func(string, []*milvuspb.PrivilegeEntity, milvuspb.OperatePrivilegeGroupType) error) *IMetaTable_OperatePrivilegeGroup_Call {
_c.Call.Return(run)
return _c
}
// OperateUserRole provides a mock function with given fields: tenant, userEntity, roleEntity, operateType
func (_m *IMetaTable) OperateUserRole(tenant string, userEntity *milvuspb.UserEntity, roleEntity *milvuspb.RoleEntity, operateType milvuspb.OperateUserRoleType) error {
ret := _m.Called(tenant, userEntity, roleEntity, operateType)

View File

@ -635,7 +635,11 @@ func (c *Core) initBuiltinRoles() error {
for _, privilege := range privilegesJSON[util.RoleConfigPrivileges] {
privilegeName := privilege[util.RoleConfigPrivilege]
if !util.IsAnyWord(privilege[util.RoleConfigPrivilege]) {
privilegeName = util.PrivilegeNameForMetastore(privilege[util.RoleConfigPrivilege])
dbPrivName, err := c.getMetastorePrivilegeName(privilege[util.RoleConfigPrivilege])
if err != nil {
return errors.Wrapf(err, "failed to get metastore privilege name for: %s", privilege[util.RoleConfigPrivilege])
}
privilegeName = dbPrivName
}
err := c.meta.OperatePrivilege(util.DefaultTenant, &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: role},
@ -2503,11 +2507,8 @@ func (c *Core) isValidGrantor(entity *milvuspb.GrantorEntity, object string) err
if entity == nil {
return errors.New("the grantor entity is nil")
}
if entity.User == nil {
return errors.New("the user entity in the grantor entity is nil")
}
if entity.User.Name == "" {
return errors.New("the name in the user entity of the grantor entity is empty")
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(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))
@ -2519,17 +2520,25 @@ func (c *Core) isValidGrantor(entity *milvuspb.GrantorEntity, object string) err
if util.IsAnyWord(entity.Privilege.Name) {
return nil
}
if privilegeName := util.PrivilegeNameForMetastore(entity.Privilege.Name); privilegeName == "" {
return fmt.Errorf("not found the privilege name[%s]", entity.Privilege.Name)
}
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 {
return nil
// check object privileges for built-in privileges
if util.IsPrivilegeNameDefined(entity.Privilege.Name) {
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 {
return nil
}
}
}
// check if it is a custom privilege group
customPrivGroup, err := c.meta.IsCustomPrivilegeGroup(entity.Privilege.Name)
if err != nil {
return err
}
if customPrivGroup {
return nil
}
return fmt.Errorf("not found the privilege name[%s] in object[%s]", entity.Privilege.Name, object)
}
@ -2574,11 +2583,18 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
}
ctxLog.Debug("before PrivilegeNameForMetastore", zap.String("privilege", in.Entity.Grantor.Privilege.Name))
if !util.IsAnyWord(in.Entity.Grantor.Privilege.Name) {
in.Entity.Grantor.Privilege.Name = util.PrivilegeNameForMetastore(in.Entity.Grantor.Privilege.Name)
// set up privilege name for metastore
privName := in.Entity.Grantor.Privilege.Name
ctxLog.Debug("before PrivilegeNameForMetastore", zap.String("privilege", privName))
if !util.IsAnyWord(privName) {
dbPrivName, err := c.getMetastorePrivilegeName(privName)
if err != nil {
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
}
in.Entity.Grantor.Privilege.Name = dbPrivName
}
ctxLog.Debug("after PrivilegeNameForMetastore", zap.String("privilege", in.Entity.Grantor.Privilege.Name))
ctxLog.Debug("after PrivilegeNameForMetastore", zap.String("privilege", privName))
if in.Entity.Object.Name == commonpb.ObjectType_Global.String() {
in.Entity.ObjectName = util.AnyWord
}
@ -2626,6 +2642,22 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile
return merr.Success(), nil
}
func (c *Core) getMetastorePrivilegeName(privName string) (string, error) {
// if it is built-in privilege, return the privilege name directly
if util.IsPrivilegeNameDefined(privName) {
return util.PrivilegeNameForMetastore(privName), nil
}
// return the privilege group name if it is a custom privilege group
customGroup, err := c.meta.IsCustomPrivilegeGroup(privName)
if err != nil {
return "", err
}
if customGroup {
return util.PrivilegeGroupNameForMetastore(privName), nil
}
return "", errors.New("not found the privilege name")
}
// SelectGrant select grant
// - check the node health
// - check if the principal entity is valid
@ -2718,14 +2750,23 @@ func (c *Core) ListPolicy(ctx context.Context, in *internalpb.ListPolicyRequest)
Status: merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_ListPolicyFailure),
}, nil
}
privGroups, err := c.meta.ListPrivilegeGroups()
if err != nil {
errMsg := "fail to list privilege groups"
ctxLog.Warn(errMsg, zap.Error(err))
return &internalpb.ListPolicyResponse{
Status: merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_ListPolicyFailure),
}, nil
}
ctxLog.Debug(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return &internalpb.ListPolicyResponse{
Status: merr.Success(),
PolicyInfos: policies,
UserRoles: userRoles,
Status: merr.Success(),
PolicyInfos: policies,
UserRoles: userRoles,
PrivilegeGroups: privGroups,
}, nil
}
@ -2926,3 +2967,236 @@ func (c *Core) CheckHealth(ctx context.Context, in *milvuspb.CheckHealthRequest)
return &milvuspb.CheckHealthResponse{Status: merr.Success(), IsHealthy: true, Reasons: []string{}}, nil
}
func (c *Core) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest) (*commonpb.Status, error) {
method := "CreatePrivilegeGroup"
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.TotalLabel).Inc()
tr := timerecord.NewTimeRecorder(method)
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 err := c.meta.CreatePrivilegeGroup(in.GroupName); err != nil {
ctxLog.Warn("fail to create privilege group", zap.Error(err))
return merr.Status(err), nil
}
ctxLog.Debug(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Inc()
return merr.Success(), nil
}
func (c *Core) DropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest) (*commonpb.Status, error) {
method := "DropPrivilegeGroup"
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.TotalLabel).Inc()
tr := timerecord.NewTimeRecorder(method)
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 err := c.meta.DropPrivilegeGroup(in.GroupName); err != nil {
ctxLog.Warn("fail to drop privilege group", zap.Error(err))
return merr.Status(err), nil
}
ctxLog.Debug(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Desc()
return merr.Success(), nil
}
func (c *Core) ListPrivilegeGroups(ctx context.Context, in *milvuspb.ListPrivilegeGroupsRequest) (*milvuspb.ListPrivilegeGroupsResponse, error) {
method := "ListPrivilegeGroups"
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.TotalLabel).Inc()
tr := timerecord.NewTimeRecorder(method)
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 &milvuspb.ListPrivilegeGroupsResponse{
Status: merr.Status(err),
}, nil
}
privGroups, err := c.meta.ListPrivilegeGroups()
if err != nil {
ctxLog.Warn("fail to list privilege group", zap.Error(err))
return &milvuspb.ListPrivilegeGroupsResponse{
Status: merr.StatusWithErrorCode(err, commonpb.ErrorCode_ListPrivilegeGroupsFailure),
}, nil
}
ctxLog.Debug(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return &milvuspb.ListPrivilegeGroupsResponse{
Status: merr.Success(),
PrivilegeGroups: privGroups,
}, nil
}
func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest) (*commonpb.Status, error) {
method := "OperatePrivilegeGroup-" + in.Type.String()
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.TotalLabel).Inc()
tr := timerecord.NewTimeRecorder(method)
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
}
redoTask := newBaseRedoTask(c.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("operate privilege group", func(ctx context.Context) ([]nestedStep, error) {
groups, err := c.meta.ListPrivilegeGroups()
if err != nil && !common.IsIgnorableError(err) {
log.Warn("fail to list privilege groups", zap.Error(err))
return nil, err
}
currGroups := lo.SliceToMap(groups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges
})
// get roles granted to the group
roles, err := c.meta.GetPrivilegeGroupRoles(in.GroupName)
if err != nil {
return nil, err
}
newGroups := make(map[string][]*milvuspb.PrivilegeEntity)
for k, v := range currGroups {
if k != in.GroupName {
newGroups[k] = v
continue
}
switch in.Type {
case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup:
newPrivs := lo.Union(v, in.Privileges)
newGroups[k] = lo.UniqBy(newPrivs, func(p *milvuspb.PrivilegeEntity) string {
return p.Name
})
case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup:
newPrivs, _ := lo.Difference(v, in.Privileges)
newGroups[k] = newPrivs
default:
return nil, errors.New("invalid operate type")
}
}
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() &&
a.ObjectName == b.ObjectName &&
a.Grantor.GetUser().GetName() == b.Grantor.GetUser().GetName() &&
a.Grantor.GetPrivilege().GetName() == b.Grantor.GetPrivilege().GetName() &&
a.DbName == b.DbName
}
for _, role := range roles {
grants, err := c.meta.SelectGrant(util.DefaultTenant, &milvuspb.GrantEntity{
Role: role,
DbName: util.AnyWord,
})
if err != nil {
return nil, err
}
currGrants := c.expandPrivilegeGroups(grants, currGroups)
newGrants := c.expandPrivilegeGroups(grants, newGroups)
toRevoke := lo.Filter(currGrants, func(item *milvuspb.GrantEntity, _ int) bool {
return !lo.ContainsBy(newGrants, func(newItem *milvuspb.GrantEntity) bool {
return compareGrants(item, newItem)
})
})
toGrant := lo.Filter(newGrants, func(item *milvuspb.GrantEntity, _ int) bool {
return !lo.ContainsBy(currGrants, func(currItem *milvuspb.GrantEntity) bool {
return compareGrants(item, currItem)
})
})
rolesToRevoke = append(rolesToRevoke, toRevoke...)
rolesToGrant = append(rolesToGrant, toGrant...)
}
if len(rolesToRevoke) > 0 {
opType := int32(typeutil.CacheRevokePrivilege)
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: opType,
OpKey: funcutil.PolicyForPrivileges(rolesToRevoke),
}); err != nil {
log.Warn("fail to refresh policy info cache for revoke privileges in operate privilege group", zap.Any("in", in), zap.Error(err))
return nil, err
}
}
if len(rolesToGrant) > 0 {
opType := int32(typeutil.CacheGrantPrivilege)
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: opType,
OpKey: funcutil.PolicyForPrivileges(rolesToGrant),
}); err != nil {
log.Warn("fail to refresh policy info cache for grants privilege in operate privilege group", zap.Any("in", in), zap.Error(err))
return nil, err
}
}
return nil, nil
}))
redoTask.AddSyncStep(NewSimpleStep("operate privilege group meta data", func(ctx context.Context) ([]nestedStep, error) {
err := c.meta.OperatePrivilegeGroup(in.GroupName, in.Privileges, in.Type)
if err != nil && !common.IsIgnorableError(err) {
log.Warn("fail to operate privilege group", zap.Error(err))
}
return nil, err
}))
err := redoTask.Execute(ctx)
if err != nil {
errMsg := "fail to execute task when operate privilege group"
ctxLog.Warn(errMsg, zap.Error(err))
status := merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperatePrivilegeGroupFailure)
return status, nil
}
ctxLog.Debug(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return merr.Success(), nil
}
func (c *Core) expandPrivilegeGroups(grants []*milvuspb.GrantEntity, groups map[string][]*milvuspb.PrivilegeEntity) []*milvuspb.GrantEntity {
newGrants := []*milvuspb.GrantEntity{}
for _, grant := range grants {
if groups[grant.Grantor.Privilege.Name] == nil {
newGrants = append(newGrants, grant)
} else {
for _, priv := range groups[grant.Grantor.Privilege.Name] {
newGrants = append(newGrants, &milvuspb.GrantEntity{
Role: grant.Role,
Object: grant.Object,
ObjectName: grant.ObjectName,
Grantor: &milvuspb.GrantorEntity{
User: grant.Grantor.User,
Privilege: priv,
},
DbName: grant.DbName,
})
}
}
}
// uniq by role + object + object name + grantor user + privilege name + db name
return lo.UniqBy(newGrants, func(g *milvuspb.GrantEntity) string {
return fmt.Sprintf("%s-%s-%s-%s-%s-%s", g.Role, g.Object, g.ObjectName, g.Grantor.User, g.Grantor.Privilege.Name, g.DbName)
})
}

View File

@ -1748,6 +1748,9 @@ func TestRootCoord_RBACError(t *testing.T) {
mockMeta.SelectRoleFunc = func(tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, nil
}
mockMeta.ListPrivilegeGroupsFunc = func() ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, nil
}
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: "foo"},
@ -1879,6 +1882,9 @@ func TestRootCoord_BuiltinRoles(t *testing.T) {
mockMeta.OperatePrivilegeFunc = func(tenant string, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error {
return nil
}
mockMeta.ListPrivilegeGroupsFunc = func() ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, nil
}
err := c.initBuiltinRoles()
assert.Equal(t, nil, err)
assert.True(t, util.IsBuiltinRole(roleDbAdmin))

View File

@ -274,6 +274,22 @@ func (m *GrpcRootCoordClient) RestoreRBAC(ctx context.Context, in *milvuspb.Rest
return &commonpb.Status{}, m.Err
}
func (m *GrpcRootCoordClient) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, m.Err
}
func (m *GrpcRootCoordClient) DropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, m.Err
}
func (m *GrpcRootCoordClient) ListPrivilegeGroups(ctx context.Context, in *milvuspb.ListPrivilegeGroupsRequest, opts ...grpc.CallOption) (*milvuspb.ListPrivilegeGroupsResponse, error) {
return &milvuspb.ListPrivilegeGroupsResponse{}, m.Err
}
func (m *GrpcRootCoordClient) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return &commonpb.Status{}, m.Err
}
func (m *GrpcRootCoordClient) Close() error {
return nil
}

View File

@ -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.20241106083218-5de5d0cfb1c1
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620
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

View File

@ -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.20241106083218-5de5d0cfb1c1 h1:GFS5AxKPcEstcfJgMGxRH+l/mKA0kK1sHDOxnOqMnoA=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241106083218-5de5d0cfb1c1/go.mod h1:/6UT4zZl6awVeXLeE7UGDWZvXj3IWkRsh3mqsn0DiAs=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620 h1:0IWUDtDloift7cQHalhdjuVkL/3qSeiXFqR7MofZBkg=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20241108105827-266fb751b620/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=

View File

@ -146,6 +146,15 @@ var (
Help: "The number of roles",
})
// RootCoordNumOfPrivilegeGroups counts the number of credentials.
RootCoordNumOfPrivilegeGroups = prometheus.NewGauge(
prometheus.GaugeOpts{
Namespace: milvusNamespace,
Subsystem: typeutil.RootCoordRole,
Name: "num_of_privilege_groups",
Help: "The number of privilege groups",
})
// RootCoordTtDelay records the max time tick delay of flow graphs in DataNodes and QueryNodes.
RootCoordTtDelay = prometheus.NewGaugeVec(
prometheus.GaugeOpts{

View File

@ -21,6 +21,7 @@ import (
"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"
)
// Meta Prefix consts
@ -114,6 +115,10 @@ var (
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetFlushState.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreatePrivilegeGroup.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropPrivilegeGroup.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListPrivilegeGroups.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeOperatePrivilegeGroup.String()),
},
commonpb.ObjectType_Global.String(): {
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeAll.String()),
@ -151,6 +156,10 @@ var (
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeAlias.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListAliases.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupAdmin.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeCreatePrivilegeGroup.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropPrivilegeGroup.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListPrivilegeGroups.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeOperatePrivilegeGroup.String()),
},
commonpb.ObjectType_User.String(): {
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()),
@ -306,6 +315,9 @@ func MetaStore2API(name string) string {
func PrivilegeNameForAPI(name string) string {
_, ok := commonpb.ObjectPrivilege_value[name]
if !ok {
if strings.HasPrefix(name, PrivilegeGroupWord) {
return typeutil.After(name, PrivilegeGroupWord)
}
return ""
}
return MetaStore2API(name)
@ -327,6 +339,15 @@ func PrivilegeNameForMetastore(name string) string {
return dbPrivilege
}
// check if the name is defined by built in privileges or privilege groups in system
func IsPrivilegeNameDefined(name string) bool {
return PrivilegeNameForMetastore(name) != ""
}
func PrivilegeGroupNameForMetastore(name string) string {
return PrivilegeGroupWord + name
}
func IsAnyWord(word string) bool {
return word == AnyWord
}

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/cockroachdb/errors"
"github.com/samber/lo"
"go.uber.org/zap"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
@ -114,6 +115,16 @@ func PolicyForPrivilege(roleName string, objectType string, objectName string, p
return fmt.Sprintf(`{"PType":"p","V0":"%s","V1":"%s","V2":"%s"}`, roleName, PolicyForResource(dbName, objectType, objectName), privilege)
}
func PolicyForPrivileges(grants []*milvuspb.GrantEntity) string {
return strings.Join(lo.Map(grants, func(r *milvuspb.GrantEntity, _ int) string {
return PolicyForPrivilege(r.Role.Name, r.Object.Name, r.ObjectName, r.Grantor.Privilege.Name, r.DbName)
}), "|")
}
func PrivilegesForPolicy(policy string) []string {
return strings.Split(policy, "|")
}
func PolicyForResource(dbName string, objectType string, objectName string) string {
return fmt.Sprintf("%s-%s", objectType, CombineObjectName(dbName, objectName))
}

View File

@ -17,6 +17,7 @@ package rbac
import (
"context"
"fmt"
"testing"
"github.com/stretchr/testify/suite"
@ -41,94 +42,194 @@ func (s *PrivilegeGroupTestSuite) SetupSuite() {
paramtable.Get().Save(paramtable.Get().CommonCfg.AuthorizationEnabled.Key, "true")
}
func (s *PrivilegeGroupTestSuite) TestPrivilegeGroup() {
func (s *PrivilegeGroupTestSuite) TestBuiltinPrivilegeGroup() {
ctx := GetContext(context.Background(), "root:123456")
// test empty rbac content
// Test empty RBAC content
resp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
s.NoError(err)
s.True(merr.Ok(resp.GetStatus()))
s.Equal("", resp.GetRBACMeta().String())
// generate some rbac content
// Generate some RBAC content
roleName := "test_role"
resp1, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{
Name: roleName,
},
createRoleResp, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{Name: roleName},
})
s.NoError(err)
s.True(merr.Ok(resp1))
resp2, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
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)
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() {
ctx := GetContext(context.Background(), "root:123456")
// Helper function to operate on privilege groups
operatePrivilegeGroup := func(groupName string, operateType milvuspb.OperatePrivilegeGroupType, privileges []*milvuspb.PrivilegeEntity) {
resp, err := s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{
GroupName: groupName,
Type: operateType,
Privileges: privileges,
})
s.NoError(err)
s.True(merr.Ok(resp))
}
// Helper function to list privilege groups and return the target group and its privileges
validatePrivilegeGroup := func(groupName string, privileges int) []*milvuspb.PrivilegeEntity {
resp, err := s.Cluster.Proxy.ListPrivilegeGroups(ctx, &milvuspb.ListPrivilegeGroupsRequest{})
s.NoError(err)
for _, privGroup := range resp.PrivilegeGroups {
if privGroup.GroupName == groupName {
s.Equal(privileges, len(privGroup.Privileges))
return privGroup.Privileges
}
}
return nil
}
// create group1: query, search
createResp, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "group1",
})
s.NoError(err)
s.True(merr.Ok(createResp))
validatePrivilegeGroup("group1", 0)
operatePrivilegeGroup("group1", milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup, []*milvuspb.PrivilegeEntity{
{Name: "Query"},
{Name: "Search"},
})
validatePrivilegeGroup("group1", 2)
// grant insert to role -> role: insert
role := "role1"
createRoleResp, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{Name: role},
})
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)
// 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)
// create group2: query, delete
createResp2, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "group2",
})
s.NoError(err)
s.True(merr.Ok(createResp2))
validatePrivilegeGroup("group2", 0)
operatePrivilegeGroup("group2", milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup, []*milvuspb.PrivilegeEntity{
{Name: "Query"},
{Name: "Delete"},
})
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)
// 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{
{Name: "Query"},
{Name: "Load"},
})
validatePrivilegeGroup("group1", 3)
s.validateGrants(ctx, role, commonpb.ObjectType_Collection.String(), 3)
// 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)
// Drop the group during any role usage will cause error
dropResp, _ := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{
GroupName: "group1",
})
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)
// Drop the privilege group after revoking the privilege will succeed
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: "group2",
})
s.NoError(err)
s.True(merr.Ok(dropResp))
// Validate the group was dropped
resp, err := s.Cluster.Proxy.ListPrivilegeGroups(ctx, &milvuspb.ListPrivilegeGroupsRequest{})
s.NoError(err)
s.Equal(0, len(resp.PrivilegeGroups))
// Drop the role
dropRoleResp, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{
RoleName: role,
})
s.NoError(err)
s.True(merr.Ok(dropRoleResp))
}
func (s *PrivilegeGroupTestSuite) operatePrivilege(ctx context.Context, role, privilege, objectType string, operateType milvuspb.OperatePrivilegeType) {
resp, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: operateType,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
Role: &milvuspb.RoleEntity{Name: role},
Object: &milvuspb.ObjectEntity{Name: objectType},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "ReadOnly"},
Privilege: &milvuspb.PrivilegeEntity{Name: privilege},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp2))
s.True(merr.Ok(resp))
}
resp3, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
func (s *PrivilegeGroupTestSuite) validateGrants(ctx context.Context, roleName, objectType string, expectedCount int) {
resp, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "ReadWrite"},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp3))
resp4, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "Admin"},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp4))
resp5, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()},
Object: &milvuspb.ObjectEntity{Name: objectType},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
},
})
fmt.Println("!!!validateGrants: ", resp)
s.NoError(err)
s.True(merr.Ok(resp5.GetStatus()))
s.Len(resp5.GetEntities(), 1)
resp6, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
},
})
s.NoError(err)
s.True(merr.Ok(resp6.GetStatus()))
s.Len(resp6.GetEntities(), 2)
s.True(merr.Ok(resp.GetStatus()))
s.Len(resp.GetEntities(), expectedCount)
}
func TestPrivilegeGroup(t *testing.T) {

View File

@ -20,6 +20,7 @@ import (
"strings"
"testing"
"github.com/samber/lo"
"github.com/stretchr/testify/suite"
"google.golang.org/grpc/metadata"
@ -63,131 +64,161 @@ func GetContext(ctx context.Context, originValue string) context.Context {
func (s *RBACBackupTestSuite) TestBackup() {
ctx := GetContext(context.Background(), "root:123456")
createRole := func(name string) {
resp, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{Name: name},
})
s.NoError(err)
s.True(merr.Ok(resp))
}
operatePrivilege := func(role, privilege, objectName, dbName string, operateType milvuspb.OperatePrivilegeType) {
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_Collection.String()},
ObjectName: objectName,
DbName: dbName,
Grantor: &milvuspb.GrantorEntity{User: &milvuspb.UserEntity{Name: util.UserRoot}, Privilege: &milvuspb.PrivilegeEntity{Name: privilege}},
},
})
s.NoError(err)
s.True(merr.Ok(resp))
}
// test empty rbac content
resp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
emptyBackupRBACResp, 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(emptyBackupRBACResp.GetStatus()))
s.Equal("", emptyBackupRBACResp.GetRBACMeta().String())
// generate some rbac content
// create role test_role
roleName := "test_role"
resp1, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{
Name: roleName,
},
createRole(roleName)
// grant collection level search privilege to role test_role
operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant)
// create privielge group test_group
groupName := "test_group"
createPrivGroupResp, err := s.Cluster.Proxy.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{
GroupName: groupName,
})
s.NoError(err)
s.True(merr.Ok(resp1))
resp2, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "Search"},
},
},
s.True(merr.Ok(createPrivGroupResp))
// add query and insert privilege to group test_group
addPrivsToGroupResp, err := s.Cluster.Proxy.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{
GroupName: groupName,
Privileges: []*milvuspb.PrivilegeEntity{{Name: "Query"}, {Name: "Insert"}},
Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup,
})
s.NoError(err)
s.True(merr.Ok(resp2))
s.Equal("", resp2.GetReason())
s.True(merr.Ok(addPrivsToGroupResp))
// grant privilege group test_group to role test_role
operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Grant)
userName := "test_user"
passwd := "test_passwd"
resp3, err := s.Cluster.Proxy.CreateCredential(ctx, &milvuspb.CreateCredentialRequest{
createCredResp, err := s.Cluster.Proxy.CreateCredential(ctx, &milvuspb.CreateCredentialRequest{
Username: userName,
Password: crypto.Base64Encode(passwd),
})
s.NoError(err)
s.True(merr.Ok(resp3))
resp4, err := s.Cluster.Proxy.OperateUserRole(ctx, &milvuspb.OperateUserRoleRequest{
s.True(merr.Ok(createCredResp))
operateUserRoleResp, err := s.Cluster.Proxy.OperateUserRole(ctx, &milvuspb.OperateUserRoleRequest{
Username: userName,
RoleName: roleName,
})
s.NoError(err)
s.True(merr.Ok(resp4))
s.True(merr.Ok(operateUserRoleResp))
// test back up rbac
resp5, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
// test back up rbac, grants should contain
backupRBACResp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
s.NoError(err)
s.True(merr.Ok(resp5.GetStatus()))
s.True(merr.Ok(backupRBACResp.GetStatus()))
s.Equal(2, len(backupRBACResp.GetRBACMeta().Grants))
grants := lo.SliceToMap(backupRBACResp.GetRBACMeta().Grants, func(g *milvuspb.GrantEntity) (string, *milvuspb.GrantEntity) {
return g.Grantor.Privilege.Name, g
})
s.True(grants["Search"] != nil)
s.True(grants[groupName] != nil)
s.Equal(groupName, backupRBACResp.GetRBACMeta().PrivilegeGroups[0].GroupName)
s.Equal(2, len(backupRBACResp.GetRBACMeta().PrivilegeGroups[0].Privileges))
// test restore, expect to failed due to role/user already exist
resp6, err := s.Cluster.Proxy.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: resp5.GetRBACMeta(),
restoreRBACResp, err := s.Cluster.Proxy.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: backupRBACResp.GetRBACMeta(),
})
s.NoError(err)
s.False(merr.Ok(resp6))
s.False(merr.Ok(restoreRBACResp))
// drop exist role/user, successful to restore
resp7, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Revoke,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "Search"},
},
},
// revoke privilege search from role test_role before dropping the role
operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke)
// revoke privilege group test_group from role test_role before dropping the role
operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke)
// drop privilege group test_group
dropPrivGroupResp, err := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{
GroupName: groupName,
})
s.NoError(err)
s.True(merr.Ok(resp7))
resp8, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{
s.True(merr.Ok(dropPrivGroupResp))
// drop role test_role
dropRoleResp, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{
RoleName: roleName,
})
s.NoError(err)
s.True(merr.Ok(resp8))
resp9, err := s.Cluster.Proxy.DeleteCredential(ctx, &milvuspb.DeleteCredentialRequest{
s.True(merr.Ok(dropRoleResp))
// delete credential
delCredResp, err := s.Cluster.Proxy.DeleteCredential(ctx, &milvuspb.DeleteCredentialRequest{
Username: userName,
})
s.NoError(err)
s.True(merr.Ok(resp9))
s.True(merr.Ok(delCredResp))
resp10, err := s.Cluster.Proxy.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: resp5.GetRBACMeta(),
// restore rbac
restoreRBACResp, err = s.Cluster.Proxy.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: backupRBACResp.GetRBACMeta(),
})
s.NoError(err)
s.True(merr.Ok(resp10))
s.True(merr.Ok(restoreRBACResp))
// check the restored rbac, should be same as the original one
resp11, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
backupRBACResp2, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
s.NoError(err)
s.True(merr.Ok(resp11.GetStatus()))
s.Equal(resp11.GetRBACMeta().String(), resp5.GetRBACMeta().String())
s.True(merr.Ok(backupRBACResp2.GetStatus()))
s.Equal(backupRBACResp2.GetRBACMeta().String(), backupRBACResp.GetRBACMeta().String())
// clean rbac meta
resp12, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Revoke,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "Search"},
},
},
operatePrivilege(roleName, "Search", util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke)
operatePrivilege(roleName, groupName, util.AnyWord, util.AnyWord, milvuspb.OperatePrivilegeType_Revoke)
dropPrivGroupResp2, err := s.Cluster.Proxy.DropPrivilegeGroup(ctx, &milvuspb.DropPrivilegeGroupRequest{
GroupName: groupName,
})
s.NoError(err)
s.True(merr.Ok(resp12))
s.True(merr.Ok(dropPrivGroupResp2))
resp13, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{
dropRoleResp2, err := s.Cluster.Proxy.DropRole(ctx, &milvuspb.DropRoleRequest{
RoleName: roleName,
})
s.NoError(err)
s.True(merr.Ok(resp13))
s.True(merr.Ok(dropRoleResp2))
resp14, err := s.Cluster.Proxy.DeleteCredential(ctx, &milvuspb.DeleteCredentialRequest{
delCredResp2, err := s.Cluster.Proxy.DeleteCredential(ctx, &milvuspb.DeleteCredentialRequest{
Username: userName,
})
s.NoError(err)
s.True(merr.Ok(resp14))
s.True(merr.Ok(delCredResp2))
}
func TestRBACBackup(t *testing.T) {