enhance: support rbac with WAL-based DDL framework (#44735)

issue: #43897

- RBAC(Roles/Users/Privileges/Privilege Groups) is implemented by
WAL-based DDL framework now.
- Support following message type in wal `AlterUser`, `DropUser`,
`AlterRole`, `DropRole`, `AlterUserRole`, `DropUserRole`,
`AlterPrivilege`, `DropPrivilege`, `AlterPrivilegeGroup`,
`DropPrivilegeGroup`, `RestoreRBAC`.
- RBAC can be synced by new CDC now.
- Refactor some UT for RBAC.

---------

Signed-off-by: chyezh <chyezh@outlook.com>
This commit is contained in:
Zhen Ye 2025-10-16 16:02:01 +08:00 committed by GitHub
parent b13b4dabbe
commit 80bb09f7c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 2942 additions and 1487 deletions

View File

@ -40,8 +40,6 @@ type RootCoordCatalog interface {
// GetCredential gets the credential info for the username, returns error if no credential exists for this username.
GetCredential(ctx context.Context, username string) (*model.Credential, error)
// CreateCredential creates credential by Username and EncryptedPassword in crediential. Please make sure credential.Username isn't empty before calling this API. Credentials already exists will be altered.
CreateCredential(ctx context.Context, credential *model.Credential) error
// AlterCredential does exactly the same as CreateCredential
AlterCredential(ctx context.Context, credential *model.Credential) error
// DropCredential removes the credential of this username

View File

@ -346,9 +346,11 @@ func (kc *Catalog) CreateAlias(ctx context.Context, alias *model.Alias, ts typeu
return kc.Snapshot.MultiSaveAndRemove(ctx, kvs, []string{oldKBefore210, oldKeyWithoutDb}, ts)
}
func (kc *Catalog) CreateCredential(ctx context.Context, credential *model.Credential) error {
func (kc *Catalog) AlterCredential(ctx context.Context, credential *model.Credential) error {
k := fmt.Sprintf("%s/%s", CredentialPrefix, credential.Username)
v, err := json.Marshal(&internalpb.CredentialInfo{EncryptedPassword: credential.EncryptedPassword})
credentialInfo := model.MarshalCredentialModel(credential)
credentialInfo.Username = "" // Username is already save in the key, remove it from the value.
v, err := json.Marshal(credentialInfo)
if err != nil {
log.Ctx(ctx).Error("create credential marshal fail", zap.String("key", k), zap.Error(err))
return err
@ -359,14 +361,9 @@ func (kc *Catalog) CreateCredential(ctx context.Context, credential *model.Crede
log.Ctx(ctx).Error("create credential persist meta fail", zap.String("key", k), zap.Error(err))
return err
}
return nil
}
func (kc *Catalog) AlterCredential(ctx context.Context, credential *model.Credential) error {
return kc.CreateCredential(ctx, credential)
}
func (kc *Catalog) listPartitionsAfter210(ctx context.Context, collectionID typeutil.UniqueID, ts typeutil.Timestamp) ([]*model.Partition, error) {
prefix := BuildPartitionPrefix(collectionID)
_, values, err := kc.Snapshot.LoadWithPrefix(ctx, prefix, ts)
@ -625,8 +622,9 @@ func (kc *Catalog) GetCredential(ctx context.Context, username string) (*model.C
if err != nil {
return nil, fmt.Errorf("unmarshal credential info err:%w", err)
}
return &model.Credential{Username: username, EncryptedPassword: credentialInfo.EncryptedPassword}, nil
// we don't save the username in the credential info, so we need to set it manually from path.
credentialInfo.Username = username
return model.UnmarshalCredentialModel(&credentialInfo), nil
}
func (kc *Catalog) AlterAlias(ctx context.Context, alias *model.Alias, ts typeutil.Timestamp) error {
@ -1050,18 +1048,6 @@ func (kc *Catalog) ListCredentialsWithPasswd(ctx context.Context) (map[string]st
return users, nil
}
func (kc *Catalog) save(ctx context.Context, k string) error {
var err error
if _, err = kc.Txn.Load(ctx, k); err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) {
return err
}
if err == nil {
log.Ctx(ctx).Debug("the key has existed", zap.String("key", k))
return common.NewIgnorableError(fmt.Errorf("the key[%s] has existed", k))
}
return kc.Txn.Save(ctx, k, "")
}
func (kc *Catalog) remove(ctx context.Context, k string) error {
var err error
if _, err = kc.Txn.Load(ctx, k); err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) {
@ -1076,11 +1062,7 @@ func (kc *Catalog) remove(ctx context.Context, k string) error {
func (kc *Catalog) CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
k := funcutil.HandleTenantForEtcdKey(RolePrefix, tenant, entity.Name)
err := kc.save(ctx, k)
if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to save the role", zap.String("key", k), zap.Error(err))
}
return err
return kc.Txn.Save(ctx, k, "")
}
func (kc *Catalog) DropRole(ctx context.Context, tenant string, roleName string) error {
@ -1112,21 +1094,13 @@ func (kc *Catalog) DropRole(ctx context.Context, tenant string, roleName string)
func (kc *Catalog) AlterUserRole(ctx context.Context, tenant string, userEntity *milvuspb.UserEntity, roleEntity *milvuspb.RoleEntity, operateType milvuspb.OperateUserRoleType) error {
k := funcutil.HandleTenantForEtcdKey(RoleMappingPrefix, tenant, fmt.Sprintf("%s/%s", userEntity.Name, roleEntity.Name))
var err error
if operateType == milvuspb.OperateUserRoleType_AddUserToRole {
err = kc.save(ctx, k)
if err != nil {
log.Ctx(ctx).Error("fail to save the user-role", zap.String("key", k), zap.Error(err))
}
} else if operateType == milvuspb.OperateUserRoleType_RemoveUserFromRole {
err = kc.remove(ctx, k)
if err != nil {
log.Ctx(ctx).Error("fail to remove the user-role", zap.String("key", k), zap.Error(err))
}
} else {
err = fmt.Errorf("invalid operate user role type, operate type: %d", operateType)
switch operateType {
case milvuspb.OperateUserRoleType_AddUserToRole:
return kc.Txn.Save(ctx, k, "")
case milvuspb.OperateUserRoleType_RemoveUserFromRole:
return kc.Txn.Remove(ctx, k)
}
return err
return fmt.Errorf("invalid operate user role type, operate type: %d", operateType)
}
func (kc *Catalog) ListRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
@ -1593,145 +1567,49 @@ func (kc *Catalog) BackupRBAC(ctx context.Context, tenant string) (*milvuspb.RBA
}
func (kc *Catalog) RestoreRBAC(ctx context.Context, tenant string, meta *milvuspb.RBACMeta) error {
var err error
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.Ctx(ctx).Warn("failed to restore rbac, try to rollback", zap.Error(err))
// roll back role
for _, role := range needRollbackRole {
err = kc.DropRole(ctx, tenant, role.GetName())
if err != nil {
log.Ctx(ctx).Warn("failed to rollback roles after restore failed", zap.Error(err))
}
}
// roll back grant
for _, grant := range needRollbackGrants {
err = kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Revoke)
if err != nil {
log.Ctx(ctx).Warn("failed to rollback grants after restore failed", zap.Error(err))
}
}
for _, user := range needRollbackUser {
// roll back user
err = kc.DropCredential(ctx, user.GetUser())
if err != nil {
log.Ctx(ctx).Warn("failed to rollback users after restore failed", zap.Error(err))
}
}
// roll back privilege group
for _, group := range needRollbackPrivilegeGroups {
err = kc.DropPrivilegeGroup(ctx, group.GetGroupName())
if err != nil {
log.Ctx(ctx).Warn("failed to rollback privilege groups after restore failed", zap.Error(err))
}
}
}
}()
// restore role
existRoles, err := kc.ListRole(ctx, tenant, nil, false)
if err != nil {
return err
}
existRoleMap := lo.SliceToMap(existRoles, func(entity *milvuspb.RoleResult) (string, struct{}) { return entity.GetRole().GetName(), struct{}{} })
for _, role := range meta.GetRoles() {
if _, ok := existRoleMap[role.GetName()]; ok {
log.Ctx(ctx).Warn("failed to restore, role already exists", zap.String("role", role.GetName()))
err = errors.Newf("role [%s] already exists", role.GetName())
return err
if err := kc.CreateRole(ctx, tenant, role); err != nil {
return errors.Wrap(err, "failed to create role")
}
err = kc.CreateRole(ctx, tenant, role)
if err != nil {
return err
}
needRollbackRole = append(needRollbackRole, role)
}
// 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.GetGroupName(), struct{}{} })
for _, group := range meta.GetPrivilegeGroups() {
if _, ok := existPrivGroupMap[group.GetGroupName()]; ok {
log.Ctx(ctx).Warn("failed to restore, privilege group already exists", zap.String("group", group.GetGroupName()))
err = errors.Newf("privilege group [%s] already exists", group.GetGroupName())
return err
if err := kc.SavePrivilegeGroup(ctx, group); err != nil {
return errors.Wrap(err, "failed to save privilege group")
}
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.GetGroupName(), struct{}{} })
for _, grant := range meta.GetGrants() {
privName := grant.GetGrantor().GetPrivilege().GetName()
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.Ctx(ctx).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
grant.Grantor.Privilege.Name = util.PrivilegeGroupNameForMetastore(privName)
}
err = kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Grant)
if err != nil {
return err
if err := kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Grant); err != nil {
return errors.Wrap(err, "failed to alter grant")
}
needRollbackGrants = append(needRollbackGrants, grant)
}
// need rollback user
existUser, err := kc.ListUser(ctx, tenant, nil, false)
if err != nil {
return err
}
existUserMap := lo.SliceToMap(existUser, func(entity *milvuspb.UserResult) (string, struct{}) { return entity.GetUser().GetName(), struct{}{} })
for _, user := range meta.GetUsers() {
if _, ok := existUserMap[user.GetUser()]; ok {
log.Ctx(ctx).Info("failed to restore, user already exists", zap.String("user", user.GetUser()))
err = errors.Newf("user [%s] already exists", user.GetUser())
return err
}
// restore user
err = kc.CreateCredential(ctx, &model.Credential{
if err := kc.AlterCredential(ctx, &model.Credential{
Username: user.GetUser(),
EncryptedPassword: user.GetPassword(),
})
if err != nil {
return err
}); err != nil {
return errors.Wrap(err, "failed to alter credential")
}
needRollbackUser = append(needRollbackUser, user)
// restore user role mapping
entity := &milvuspb.UserEntity{
Name: user.GetUser(),
}
for _, role := range user.GetRoles() {
err = kc.AlterUserRole(ctx, tenant, entity, role, milvuspb.OperateUserRoleType_AddUserToRole)
if err != nil {
return err
if err := kc.AlterUserRole(ctx, tenant, entity, role, milvuspb.OperateUserRoleType_AddUserToRole); err != nil {
return errors.Wrap(err, "failed to alter user role")
}
}
}
return err
return nil
}
func (kc *Catalog) GetPrivilegeGroup(ctx context.Context, groupName string) (*milvuspb.PrivilegeGroupInfo, error) {

View File

@ -1564,7 +1564,7 @@ func TestRBAC_Credential(t *testing.T) {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.CreateCredential(ctx, &model.Credential{
err := c.AlterCredential(ctx, &model.Credential{
Username: test.user,
EncryptedPassword: test.password,
})
@ -1772,94 +1772,17 @@ func TestRBAC_Role(t *testing.T) {
})
}
})
t.Run("test save", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = NewCatalog(kvmock, nil).(*Catalog)
notExistKey = "not-exist"
errorKey = "error"
otherError = errors.New("mock load error")
)
kvmock.EXPECT().Load(mock.Anything, notExistKey).Return("", merr.WrapErrIoKeyNotFound(notExistKey)).Once()
kvmock.EXPECT().Load(mock.Anything, errorKey).Return("", otherError).Once()
kvmock.EXPECT().Load(mock.Anything, mock.Anything).Return("", nil).Once()
kvmock.EXPECT().Save(mock.Anything, mock.Anything, mock.Anything).Call.Return(nil).Once()
tests := []struct {
description string
isValid bool
key string
expectedError error
ignorable bool
}{
{"key not exists", true, notExistKey, nil, false},
{"other error", false, errorKey, otherError, false},
{"ignorable error", false, "key1", &common.IgnorableError{}, true},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.save(ctx, test.key)
if test.isValid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
if test.ignorable {
_, ok := err.(*common.IgnorableError)
assert.True(t, ok)
}
})
}
})
t.Run("test CreateRole", func(t *testing.T) {
var (
kvmock = mocks.NewTxnKV(t)
c = NewCatalog(kvmock, nil)
notExistName = "not-exist"
notExistPath = funcutil.HandleTenantForEtcdKey(RolePrefix, tenant, notExistName)
errorName = "error"
errorPath = funcutil.HandleTenantForEtcdKey(RolePrefix, tenant, errorName)
otherError = errors.New("mock load error")
)
kvmock.EXPECT().Load(mock.Anything, notExistPath).Return("", merr.WrapErrIoKeyNotFound(notExistName)).Once()
kvmock.EXPECT().Load(mock.Anything, errorPath).Return("", otherError).Once()
kvmock.EXPECT().Load(mock.Anything, mock.Anything).Return("", nil).Once()
kvmock.EXPECT().Save(mock.Anything, mock.Anything, mock.Anything).Call.Return(nil).Once()
tests := []struct {
description string
isValid bool
name string
expectedError error
}{
{"key not exists", true, notExistName, nil},
{"other error", false, errorName, otherError},
{"ignorable error", false, "key1", &common.IgnorableError{}},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.CreateRole(ctx, tenant, &milvuspb.RoleEntity{
Name: test.name,
})
if test.isValid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
err := c.CreateRole(ctx, tenant, &milvuspb.RoleEntity{
Name: "role1",
})
assert.NoError(t, err)
})
t.Run("test DropRole", func(t *testing.T) {
var (
@ -1920,58 +1843,17 @@ func TestRBAC_Role(t *testing.T) {
c = NewCatalog(kvmock, nil)
user = "default-user"
noErrorRoleSave = "no-error-role-save"
noErrorRoleSavepath = funcutil.HandleTenantForEtcdKey(RoleMappingPrefix, tenant, fmt.Sprintf("%s/%s", user, noErrorRoleSave))
errorRoleSave = "error-role-save"
errorRoleSavepath = funcutil.HandleTenantForEtcdKey(RoleMappingPrefix, tenant, fmt.Sprintf("%s/%s", user, errorRoleSave))
errorRoleRemove = "error-role-remove"
errorRoleRemovepath = funcutil.HandleTenantForEtcdKey(RoleMappingPrefix, tenant, fmt.Sprintf("%s/%s", user, errorRoleRemove))
)
kvmock.EXPECT().Save(mock.Anything, mock.Anything, mock.Anything).Return(nil)
kvmock.EXPECT().Remove(mock.Anything, mock.Anything).Return(nil)
// Catalog.save() returns error
kvmock.EXPECT().Load(mock.Anything, errorRoleSavepath).Return("", nil)
// Catalog.save() returns nil
kvmock.EXPECT().Load(mock.Anything, noErrorRoleSavepath).Return("", merr.WrapErrIoKeyNotFound(noErrorRoleSavepath))
// Catalog.remove() returns error
kvmock.EXPECT().Load(mock.Anything, errorRoleRemovepath).Return("", errors.New("not exists"))
// Catalog.remove() returns nil
kvmock.EXPECT().Load(mock.Anything, mock.Anything).Return("", nil)
tests := []struct {
description string
isValid bool
role string
oType milvuspb.OperateUserRoleType
}{
{"valid role role1, AddUserToRole", true, noErrorRoleSave, milvuspb.OperateUserRoleType_AddUserToRole},
{"invalid role error-role, AddUserToRole", false, errorRoleSave, milvuspb.OperateUserRoleType_AddUserToRole},
{"valid role role1, RemoveUserFromRole", true, "role", milvuspb.OperateUserRoleType_RemoveUserFromRole},
{"invalid role error-role, RemoveUserFromRole", false, errorRoleRemove, milvuspb.OperateUserRoleType_RemoveUserFromRole},
{"invalid operate type 100", false, "role1", milvuspb.OperateUserRoleType(100)},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := c.AlterUserRole(ctx, tenant, &milvuspb.UserEntity{Name: user}, &milvuspb.RoleEntity{
Name: test.role,
}, test.oType)
if test.isValid {
assert.NoError(t, err)
} else {
assert.Error(t, err)
}
})
}
err := c.AlterUserRole(ctx, tenant, &milvuspb.UserEntity{Name: user}, &milvuspb.RoleEntity{
Name: "role1",
}, milvuspb.OperateUserRoleType_RemoveUserFromRole)
require.NoError(t, err)
err = c.AlterUserRole(ctx, tenant, &milvuspb.UserEntity{Name: user}, &milvuspb.RoleEntity{
Name: "role1",
}, milvuspb.OperateUserRoleType_AddUserToRole)
require.NoError(t, err)
})
t.Run("test ListRole", func(t *testing.T) {
@ -2816,7 +2698,7 @@ func TestRBAC_Backup(t *testing.T) {
Privilege: &milvuspb.PrivilegeEntity{Name: "PrivilegeLoad"},
},
}, milvuspb.OperatePrivilegeType_Grant)
c.CreateCredential(ctx, &model.Credential{
c.AlterCredential(ctx, &model.Credential{
Username: "user1",
EncryptedPassword: "passwd",
})
@ -2940,15 +2822,6 @@ func TestRBAC_Restore(t *testing.T) {
},
},
},
{
User: "user1",
Password: "passwd",
Roles: []*milvuspb.RoleEntity{
{
Name: "role2",
},
},
},
},
Roles: []*milvuspb.RoleEntity{
{
@ -2979,30 +2852,31 @@ func TestRBAC_Restore(t *testing.T) {
// test restore failed and roll back
err = c.RestoreRBAC(ctx, util.DefaultTenant, rbacMeta2)
assert.Error(t, err)
assert.NoError(t, err)
// check user
users, err = c.ListCredentialsWithPasswd(ctx)
assert.NoError(t, err)
assert.Len(t, users, 1)
assert.Len(t, users, 2)
// check role
roles, err = c.ListRole(ctx, util.DefaultTenant, nil, false)
assert.NoError(t, err)
assert.Len(t, roles, 1)
assert.Len(t, roles, 2)
// check grant
grants, err = c.ListGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: roles[0].Role,
Role: &milvuspb.RoleEntity{Name: "role2"},
DbName: util.AnyWord,
})
assert.NoError(t, err)
assert.Len(t, grants, 1)
assert.Equal(t, grants[0].Grantor.Privilege.Name, "Load")
assert.Equal(t, "obj_name2", grants[0].ObjectName)
assert.Equal(t, "role2", grants[0].Role.Name)
assert.Equal(t, "user2", 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)
assert.Len(t, privGroups, 2)
}
func TestRBAC_PrivilegeGroup(t *testing.T) {

View File

@ -8,6 +8,7 @@ type Credential struct {
Tenant string
IsSuper bool
Sha256Password string
TimeTick uint64 // the timetick in wal which the credential updates
}
func MarshalCredentialModel(cred *Credential) *internalpb.CredentialInfo {
@ -20,5 +21,20 @@ func MarshalCredentialModel(cred *Credential) *internalpb.CredentialInfo {
EncryptedPassword: cred.EncryptedPassword,
IsSuper: cred.IsSuper,
Sha256Password: cred.Sha256Password,
TimeTick: cred.TimeTick,
}
}
func UnmarshalCredentialModel(cred *internalpb.CredentialInfo) *Credential {
if cred == nil {
return nil
}
return &Credential{
Username: cred.Username,
EncryptedPassword: cred.EncryptedPassword,
Tenant: cred.Tenant,
IsSuper: cred.IsSuper,
Sha256Password: cred.Sha256Password,
TimeTick: cred.TimeTick,
}
}

View File

@ -0,0 +1,104 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"fmt"
"github.com/cockroachdb/errors"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/broadcast"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
"github.com/milvus-io/milvus/internal/util/proxyutil"
"github.com/milvus-io/milvus/pkg/v2/proto/messagespb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message/ce"
)
// RegisterDDLCallbacks registers the ddl callbacks.
func RegisterDDLCallbacks(core *Core) {
ddlCallback := &DDLCallback{
Core: core,
}
// RBAC
ddlCallback.registerRBACCallbacks()
}
// registerRBACCallbacks registers the rbac callbacks.
func (c *DDLCallback) registerRBACCallbacks() {
registry.RegisterAlterUserV2AckCallback(c.alterUserV2AckCallback)
registry.RegisterDropUserV2AckCallback(c.dropUserV2AckCallback)
registry.RegisterAlterRoleV2AckCallback(c.alterRoleV2AckCallback)
registry.RegisterDropRoleV2AckCallback(c.dropRoleV2AckCallback)
registry.RegisterAlterUserRoleV2AckCallback(c.alterUserRoleV2AckCallback)
registry.RegisterDropUserRoleV2AckCallback(c.dropUserRoleV2AckCallback)
registry.RegisterAlterPrivilegeV2AckCallback(c.alterPrivilegeV2AckCallback)
registry.RegisterDropPrivilegeV2AckCallback(c.dropPrivilegeV2AckCallback)
registry.RegisterAlterPrivilegeGroupV2AckCallback(c.alterPrivilegeGroupV2AckCallback)
registry.RegisterDropPrivilegeGroupV2AckCallback(c.dropPrivilegeGroupV2AckCallback)
registry.RegisterRestoreRBACV2AckCallback(c.restoreRBACV2AckCallback)
}
// DDLCallback is the callback of ddl.
type DDLCallback struct {
*Core
}
// CacheExpirationsGetter is the getter of cache expirations.
type CacheExpirationsGetter interface {
GetCacheExpirations() *message.CacheExpirations
}
// ExpireCaches handles the cache
func (c *DDLCallback) ExpireCaches(ctx context.Context, expirations any, timetick uint64) error {
var cacheExpirations *message.CacheExpirations
if g, ok := expirations.(CacheExpirationsGetter); ok {
cacheExpirations = g.GetCacheExpirations()
} else if g, ok := expirations.(*message.CacheExpirations); ok {
cacheExpirations = g
} else if g, ok := expirations.(*ce.CacheExpirationsBuilder); ok {
cacheExpirations = g.Build()
} else {
panic(fmt.Sprintf("invalid getter type: %T", expirations))
}
for _, cacheExpiration := range cacheExpirations.CacheExpirations {
if err := c.expireCache(ctx, cacheExpiration, timetick); err != nil {
return err
}
}
return nil
}
func (c *DDLCallback) expireCache(ctx context.Context, cacheExpiration *message.CacheExpiration, timetick uint64) error {
switch cacheExpiration.Cache.(type) {
case *messagespb.CacheExpiration_LegacyProxyCollectionMetaCache:
legacyProxyCollectionMetaCache := cacheExpiration.GetLegacyProxyCollectionMetaCache()
return c.Core.ExpireMetaCache(ctx, legacyProxyCollectionMetaCache.DbName, []string{legacyProxyCollectionMetaCache.CollectionName}, legacyProxyCollectionMetaCache.CollectionId, legacyProxyCollectionMetaCache.PartitionName, timetick, proxyutil.SetMsgType(legacyProxyCollectionMetaCache.MsgType))
}
return nil
}
// startBroadcastWithRBACLock starts a broadcast for rbac.
func startBroadcastWithRBACLock(ctx context.Context) (broadcaster.BroadcastAPI, error) {
api, err := broadcast.StartBroadcastWithResourceKeys(ctx, message.NewExclusivePrivilegeResourceKey())
if err != nil {
return nil, errors.Wrap(err, "failed to start broadcast with rbac lock")
}
return api, nil
}

View File

@ -0,0 +1,137 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"strings"
"github.com/cockroachdb/errors"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/distributed/streaming"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
// broadcastAlterUserForCreateCredential broadcasts the alter user message for create credential.
func (c *Core) broadcastAlterUserForCreateCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
credInfo.Username = strings.TrimSpace(credInfo.Username)
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfAddCredential(ctx, credInfo); err != nil {
return errors.Wrap(err, "failed to check if add credential")
}
msg := message.NewAlterUserMessageBuilderV2().
WithHeader(&message.AlterUserMessageHeader{
UserEntity: &milvuspb.UserEntity{Name: credInfo.Username},
}).
WithBody(&message.AlterUserMessageBody{
CredentialInfo: credInfo,
}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
// broadcastAlterUserForUpdateCredential broadcasts the alter user message for update credential.
func (c *Core) broadcastAlterUserForUpdateCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
credInfo.Username = strings.TrimSpace(credInfo.Username)
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfUpdateCredential(ctx, credInfo); err != nil {
return errors.Wrap(err, "failed to check if update credential")
}
msg := message.NewAlterUserMessageBuilderV2().
WithHeader(&message.AlterUserMessageHeader{
UserEntity: &milvuspb.UserEntity{Name: credInfo.Username},
}).
WithBody(&message.AlterUserMessageBody{
CredentialInfo: credInfo,
}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
// alterUserV2AckCallback is the ack callback function for the AlterUserMessageV2 message.
func (c *DDLCallback) alterUserV2AckCallback(ctx context.Context, result message.BroadcastResultAlterUserMessageV2) error {
// insert to db
if err := c.meta.AlterCredential(ctx, result); err != nil {
return errors.Wrap(err, "failed to alter credential")
}
// update proxy's local cache
if err := c.UpdateCredCache(ctx, result.Message.MustBody().CredentialInfo); err != nil {
return errors.Wrap(err, "failed to update cred cache")
}
return nil
}
// broadcastDropUserForDeleteCredential broadcasts the drop user message for delete credential.
func (c *Core) broadcastDropUserForDeleteCredential(ctx context.Context, in *milvuspb.DeleteCredentialRequest) error {
in.Username = strings.TrimSpace(in.Username)
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfDeleteCredential(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if delete credential")
}
msg := message.NewDropUserMessageBuilderV2().
WithHeader(&message.DropUserMessageHeader{
UserName: in.Username,
}).
WithBody(&message.DropUserMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
// dropUserV2AckCallback is the ack callback function for the DeleteCredential message
func (c *DDLCallback) dropUserV2AckCallback(ctx context.Context, result message.BroadcastResultDropUserMessageV2) error {
if err := c.meta.DeleteCredential(ctx, result); err != nil {
return errors.Wrap(err, "failed to delete credential")
}
if err := c.ExpireCredCache(ctx, result.Message.Header().UserName); err != nil {
return errors.Wrap(err, "failed to expire cred cache")
}
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheDeleteUser),
OpKey: result.Message.Header().UserName,
}); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}

View File

@ -0,0 +1,106 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/rootcoordpb"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
)
func TestDDLCallbacksRBACCredential(t *testing.T) {
initStreamingSystem()
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
testUserName := "user" + funcutil.RandomString(10)
core := newTestCore(withHealthyCode(),
withMeta(&MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}),
withValidProxyManager(),
)
registry.ResetRegistration()
RegisterDDLCallbacks(core)
// Delete a not existed credential should succeed
status, err := core.DeleteCredential(context.Background(), &milvuspb.DeleteCredentialRequest{
Username: testUserName,
})
require.NoError(t, merr.CheckRPCCall(status, err))
// Update a not existed credential should return error.
status, err = core.UpdateCredential(context.Background(), &internalpb.CredentialInfo{
Username: testUserName,
EncryptedPassword: "123456",
})
require.Error(t, merr.CheckRPCCall(status, err))
// Create a new credential.
status, err = core.CreateCredential(context.Background(), &internalpb.CredentialInfo{
Username: testUserName,
EncryptedPassword: "123456",
})
require.NoError(t, merr.CheckRPCCall(status, err))
getCredentialResp, err := core.GetCredential(context.Background(), &rootcoordpb.GetCredentialRequest{
Username: testUserName,
})
require.NoError(t, merr.CheckRPCCall(getCredentialResp.Status, err))
assert.Equal(t, "123456", getCredentialResp.Password)
// Create a new credential with same username should return error.
status, err = core.CreateCredential(context.Background(), &internalpb.CredentialInfo{
Username: testUserName,
EncryptedPassword: "123456",
})
require.Error(t, merr.CheckRPCCall(status, err))
// Update the created credential.
status, err = core.UpdateCredential(context.Background(), &internalpb.CredentialInfo{
Username: testUserName,
EncryptedPassword: "1234567",
})
require.NoError(t, merr.CheckRPCCall(status, err))
getCredentialResp, err = core.GetCredential(context.Background(), &rootcoordpb.GetCredentialRequest{
Username: testUserName,
})
require.NoError(t, merr.CheckRPCCall(getCredentialResp.Status, err))
assert.Equal(t, "1234567", getCredentialResp.Password)
// Delete the created credential.
status, err = core.DeleteCredential(context.Background(), &milvuspb.DeleteCredentialRequest{
Username: testUserName,
})
require.NoError(t, merr.CheckRPCCall(status, err))
getCredentialResp, err = core.GetCredential(context.Background(), &rootcoordpb.GetCredentialRequest{
Username: testUserName,
})
require.Error(t, merr.CheckRPCCall(getCredentialResp.Status, err))
}

View File

@ -0,0 +1,199 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"strings"
"github.com/cockroachdb/errors"
"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/internal/distributed/streaming"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util"
)
func (c *Core) broadcastOperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivilegeRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.operatePrivilegeCommonCheck(ctx, in); err != nil {
return errors.Wrap(err, "failed to operate privilege common check")
}
privName := in.Entity.Grantor.Privilege.Name
switch in.Version {
case "v2":
if err := c.isValidPrivilegeV2(ctx, privName); err != nil {
return err
}
if err := c.validatePrivilegeGroupParams(ctx, privName, in.Entity.DbName, in.Entity.ObjectName); err != nil {
return err
}
// set up object type for metastore, to be compatible with v1 version
in.Entity.Object.Name = util.GetObjectType(privName)
default:
if err := c.isValidPrivilege(ctx, privName, in.Entity.Object.Name); err != nil {
return err
}
// set up object name if it is global object type and not built in privilege group
if in.Entity.Object.Name == commonpb.ObjectType_Global.String() && !util.IsBuiltinPrivilegeGroup(in.Entity.Grantor.Privilege.Name) {
in.Entity.ObjectName = util.AnyWord
}
}
var msg message.BroadcastMutableMessage
switch in.Type {
case milvuspb.OperatePrivilegeType_Grant:
msg = message.NewAlterPrivilegeMessageBuilderV2().
WithHeader(&message.AlterPrivilegeMessageHeader{
Entity: in.Entity,
}).
WithBody(&message.AlterPrivilegeMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
case milvuspb.OperatePrivilegeType_Revoke:
msg = message.NewDropPrivilegeMessageBuilderV2().
WithHeader(&message.DropPrivilegeMessageHeader{
Entity: in.Entity,
}).
WithBody(&message.DropPrivilegeMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
default:
return errors.New("invalid operate privilege type")
}
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *DDLCallback) alterPrivilegeV2AckCallback(ctx context.Context, result message.BroadcastResultAlterPrivilegeMessageV2) error {
return executeOperatePrivilegeTaskSteps(ctx, c.Core, result.Message.Header().Entity, milvuspb.OperatePrivilegeType_Grant)
}
func (c *DDLCallback) dropPrivilegeV2AckCallback(ctx context.Context, result message.BroadcastResultDropPrivilegeMessageV2) error {
return executeOperatePrivilegeTaskSteps(ctx, c.Core, result.Message.Header().Entity, milvuspb.OperatePrivilegeType_Revoke)
}
func (c *Core) broadcastCreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePrivilegeGroupRequest) error {
in.GroupName = strings.TrimSpace(in.GroupName)
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfPrivilegeGroupCreatable(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if privilege group creatable")
}
msg := message.NewAlterPrivilegeGroupMessageBuilderV2().
WithHeader(&message.AlterPrivilegeGroupMessageHeader{
PrivilegeGroupInfo: &milvuspb.PrivilegeGroupInfo{
GroupName: in.GroupName,
},
}).
WithBody(&message.AlterPrivilegeGroupMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *Core) broadcastOperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePrivilegeGroupRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfPrivilegeGroupAlterable(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if privilege group alterable")
}
var msg message.BroadcastMutableMessage
switch in.Type {
case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup:
msg = message.NewAlterPrivilegeGroupMessageBuilderV2().
WithHeader(&message.AlterPrivilegeGroupMessageHeader{
PrivilegeGroupInfo: &milvuspb.PrivilegeGroupInfo{
GroupName: in.GroupName,
Privileges: in.Privileges,
},
}).
WithBody(&message.AlterPrivilegeGroupMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup:
msg = message.NewDropPrivilegeGroupMessageBuilderV2().
WithHeader(&message.DropPrivilegeGroupMessageHeader{
PrivilegeGroupInfo: &milvuspb.PrivilegeGroupInfo{
GroupName: in.GroupName,
Privileges: in.Privileges,
},
}).
WithBody(&message.DropPrivilegeGroupMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
default:
return errors.New("invalid operate privilege group type")
}
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *DDLCallback) alterPrivilegeGroupV2AckCallback(ctx context.Context, result message.BroadcastResultAlterPrivilegeGroupMessageV2) error {
if len(result.Message.Header().PrivilegeGroupInfo.Privileges) == 0 {
return c.meta.CreatePrivilegeGroup(ctx, result.Message.Header().PrivilegeGroupInfo.GroupName)
}
return executeOperatePrivilegeGroupTaskSteps(ctx, c.Core, result.Message.Header().PrivilegeGroupInfo, milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup)
}
func (c *Core) broadcastDropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivilegeGroupRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfPrivilegeGroupDropable(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if privilege group dropable")
}
msg := message.NewDropPrivilegeGroupMessageBuilderV2().
WithHeader(&message.DropPrivilegeGroupMessageHeader{
PrivilegeGroupInfo: &milvuspb.PrivilegeGroupInfo{
GroupName: in.GroupName,
},
}).
WithBody(&message.DropPrivilegeGroupMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *DDLCallback) dropPrivilegeGroupV2AckCallback(ctx context.Context, result message.BroadcastResultDropPrivilegeGroupMessageV2) error {
if len(result.Message.Header().PrivilegeGroupInfo.Privileges) == 0 {
return c.meta.DropPrivilegeGroup(ctx, result.Message.Header().PrivilegeGroupInfo.GroupName)
}
return executeOperatePrivilegeGroupTaskSteps(ctx, c.Core, result.Message.Header().PrivilegeGroupInfo, milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup)
}

View File

@ -0,0 +1,211 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
)
func TestDDLCallbacksRBACPrivilege(t *testing.T) {
initStreamingSystem()
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
core := newTestCore(withHealthyCode(),
withMeta(&MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}),
withValidProxyManager(),
)
registry.ResetRegistration()
RegisterDDLCallbacks(core)
// Create a new role.
targetRoleName := "newRole"
status, err := core.CreateRole(context.Background(), &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{
Name: targetRoleName,
},
})
require.NoError(t, merr.CheckRPCCall(status, err))
targetUserName := "newUser"
status, err = core.CreateCredential(context.Background(), &internalpb.CredentialInfo{
Username: targetUserName,
EncryptedPassword: "123456",
})
require.NoError(t, merr.CheckRPCCall(status, err))
// Drop not existed privilege should return error.
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Revoke,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{
Name: targetRoleName,
},
Grantor: &milvuspb.GrantorEntity{
Privilege: &milvuspb.PrivilegeEntity{
Name: "not existed",
},
},
},
})
require.Error(t, merr.CheckRPCCall(status, err))
entity := &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{
Name: targetRoleName,
},
Object: &milvuspb.ObjectEntity{
Name: "Global",
},
ObjectName: "*",
Grantor: &milvuspb.GrantorEntity{
Privilege: &milvuspb.PrivilegeEntity{
Name: "DescribeCollection",
},
User: &milvuspb.UserEntity{
Name: targetUserName,
},
},
}
// Grant and revoke with v2 version
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: entity,
Version: "v2",
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectGrantResp, err := core.SelectGrant(context.Background(), &milvuspb.SelectGrantRequest{
Entity: entity,
})
require.NoError(t, merr.CheckRPCCall(selectGrantResp, err))
require.Equal(t, 1, len(selectGrantResp.Entities))
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Revoke,
Entity: entity,
Version: "v2",
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectGrantResp, err = core.SelectGrant(context.Background(), &milvuspb.SelectGrantRequest{
Entity: entity,
})
require.NoError(t, merr.CheckRPCCall(selectGrantResp, err))
require.Equal(t, 0, len(selectGrantResp.Entities))
// Grant and revoke with v1 version
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: entity,
Version: "v1",
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectGrantResp, err = core.SelectGrant(context.Background(), &milvuspb.SelectGrantRequest{
Entity: entity,
})
require.NoError(t, merr.CheckRPCCall(selectGrantResp, err))
require.Equal(t, 1, len(selectGrantResp.Entities))
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Revoke,
Entity: entity,
Version: "v1",
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectGrantResp, err = core.SelectGrant(context.Background(), &milvuspb.SelectGrantRequest{
Entity: entity,
})
require.NoError(t, merr.CheckRPCCall(selectGrantResp, err))
require.Equal(t, 0, len(selectGrantResp.Entities))
// Grant and try drop role should return error
status, err = core.OperatePrivilege(context.Background(), &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: entity,
Version: "v1",
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.DropRole(context.Background(), &milvuspb.DropRoleRequest{
RoleName: targetRoleName,
})
require.Error(t, merr.CheckRPCCall(status, err))
}
func TestDDLCallbacksRBACPrivilegeGroup(t *testing.T) {
initStreamingSystem()
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
core := newTestCore(withHealthyCode(),
withMeta(&MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}),
withValidProxyManager(),
)
registry.ResetRegistration()
RegisterDDLCallbacks(core)
groupName := "group1"
status, err := core.CreatePrivilegeGroup(context.Background(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: groupName,
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.OperatePrivilegeGroup(context.Background(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: groupName,
Type: milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup,
Privileges: []*milvuspb.PrivilegeEntity{{Name: "Query"}},
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.OperatePrivilegeGroup(context.Background(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: groupName,
Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup,
Privileges: []*milvuspb.PrivilegeEntity{{Name: "Query"}},
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.OperatePrivilegeGroup(context.Background(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: groupName,
Type: milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup,
Privileges: []*milvuspb.PrivilegeEntity{{Name: "Query"}},
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.DropPrivilegeGroup(context.Background(), &milvuspb.DropPrivilegeGroupRequest{
GroupName: groupName,
})
require.NoError(t, merr.CheckRPCCall(status, err))
}

View File

@ -0,0 +1,65 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"github.com/cockroachdb/errors"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/distributed/streaming"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
func (c *Core) broadcastRestoreRBACV2(ctx context.Context, req *milvuspb.RestoreRBACMetaRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfRBACRestorable(ctx, req); err != nil {
return errors.Wrap(err, "failed to check if rbac restorable")
}
msg := message.NewRestoreRBACMessageBuilderV2().
WithHeader(&message.RestoreRBACMessageHeader{}).
WithBody(&message.RestoreRBACMessageBody{
RbacMeta: req.GetRBACMeta(),
}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *DDLCallback) restoreRBACV2AckCallback(ctx context.Context, result message.BroadcastResultRestoreRBACMessageV2) error {
meta := result.Message.MustBody().RbacMeta
if err := c.meta.RestoreRBAC(ctx, util.DefaultTenant, meta); err != nil {
return errors.Wrap(err, "failed to restore rbac meta data")
}
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheRefresh),
}); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}

View File

@ -0,0 +1,177 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
)
func TestDDLCallbacksRBACRestore(t *testing.T) {
initStreamingSystem()
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
core := newTestCore(withHealthyCode(),
withMeta(&MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}),
withValidProxyManager(),
)
registry.ResetRegistration()
RegisterDDLCallbacks(core)
ctx := context.Background()
rbacMeta := &milvuspb.RBACMeta{
Users: []*milvuspb.UserInfo{
{
User: "user1",
Password: "passwd",
Roles: []*milvuspb.RoleEntity{
{
Name: "role1",
},
},
},
},
Roles: []*milvuspb.RoleEntity{
{
Name: "role1",
},
},
Grants: []*milvuspb.GrantEntity{
{
Role: &milvuspb.RoleEntity{Name: "role1"},
Object: &milvuspb.ObjectEntity{Name: "obj1"},
ObjectName: "obj_name1",
DbName: util.DefaultDBName,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "user1"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Load"},
},
},
},
PrivilegeGroups: []*milvuspb.PrivilegeGroupInfo{
{
GroupName: "custom_group",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}},
},
},
}
// test restore success
status, err := core.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: rbacMeta,
})
require.NoError(t, merr.CheckRPCCall(status, err))
status, err = core.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: rbacMeta,
})
require.Error(t, merr.CheckRPCCall(status, err))
// check user
users, err := core.meta.ListCredentialUsernames(ctx)
assert.NoError(t, err)
assert.Len(t, users.Usernames, 1)
assert.Equal(t, "user1", users.Usernames[0])
// check grant
userRoles, err := core.meta.ListUserRole(ctx, util.DefaultTenant)
assert.NoError(t, err)
assert.Len(t, userRoles, 1)
assert.Equal(t, "user1/role1", userRoles[0])
policies, err := core.meta.SelectGrant(ctx, util.DefaultTenant, rbacMeta.Grants[0])
assert.NoError(t, err)
assert.Len(t, policies, 1)
assert.Equal(t, "obj_name1", policies[0].ObjectName)
assert.Equal(t, "role1", policies[0].Role.Name)
assert.Equal(t, "user1", policies[0].Grantor.User.Name)
assert.Equal(t, "Load", policies[0].Grantor.Privilege.Name)
// check privilege group
privGroups, err := core.meta.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{
{
User: "user2",
Password: "passwd",
Roles: []*milvuspb.RoleEntity{
{
Name: "role2",
},
},
},
{
User: "user1",
Password: "passwd",
Roles: []*milvuspb.RoleEntity{
{
Name: "role2",
},
},
},
},
Roles: []*milvuspb.RoleEntity{
{
Name: "role2",
},
},
Grants: []*milvuspb.GrantEntity{
{
Role: &milvuspb.RoleEntity{Name: "role2"},
Object: &milvuspb.ObjectEntity{Name: "obj2"},
ObjectName: "obj_name2",
DbName: util.DefaultDBName,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "user2"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Load"},
},
},
},
PrivilegeGroups: []*milvuspb.PrivilegeGroupInfo{
{
GroupName: "custom_group2",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "DropCollection"}},
},
},
}
// test restore failed and roll back
status, err = core.RestoreRBAC(ctx, &milvuspb.RestoreRBACMetaRequest{
RBACMeta: rbacMeta2,
})
require.Error(t, merr.CheckRPCCall(status, err))
}

View File

@ -0,0 +1,170 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"github.com/cockroachdb/errors"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/distributed/streaming"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
func (c *Core) broadcastCreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfCreateRole(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if create role")
}
msg := message.NewAlterRoleMessageBuilderV2().
WithHeader(&message.AlterRoleMessageHeader{
RoleEntity: in.GetEntity(),
}).
WithBody(&message.AlterRoleMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
// alterRoleV2AckCallback is the ack callback function for the AlterRoleMessageV2 message.
func (c *DDLCallback) alterRoleV2AckCallback(ctx context.Context, result message.BroadcastResultAlterRoleMessageV2) error {
return c.meta.CreateRole(ctx, util.DefaultTenant, result.Message.Header().RoleEntity)
}
func (c *Core) broadcastDropRole(ctx context.Context, in *milvuspb.DropRoleRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfDropRole(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if drop role")
}
msg := message.NewDropRoleMessageBuilderV2().
WithHeader(&message.DropRoleMessageHeader{
RoleName: in.RoleName,
}).
WithBody(&message.DropRoleMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
// dropRoleV2AckCallback is the ack callback function for the DropRoleMessageV2 message.
func (c *DDLCallback) dropRoleV2AckCallback(ctx context.Context, result message.BroadcastResultDropRoleMessageV2) error {
// There should always be only one message in the msgs slice.
msg := result.Message
err := c.meta.DropRole(ctx, util.DefaultTenant, msg.Header().RoleName)
if err != nil {
return errors.Wrap(err, "failed to drop role")
}
if err := c.meta.DropGrant(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: msg.Header().RoleName}); err != nil {
return errors.Wrap(err, "failed to drop grant")
}
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheDropRole),
OpKey: msg.Header().RoleName,
}); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}
func (c *Core) broadcastOperateUserRole(ctx context.Context, in *milvuspb.OperateUserRoleRequest) error {
broadcaster, err := startBroadcastWithRBACLock(ctx)
if err != nil {
return err
}
defer broadcaster.Close()
if err := c.meta.CheckIfOperateUserRole(ctx, in); err != nil {
return errors.Wrap(err, "failed to check if operate user role")
}
var msg message.BroadcastMutableMessage
switch in.Type {
case milvuspb.OperateUserRoleType_AddUserToRole:
msg = message.NewAlterUserRoleMessageBuilderV2().
WithHeader(&message.AlterUserRoleMessageHeader{
RoleBinding: &message.RoleBinding{
UserEntity: &milvuspb.UserEntity{Name: in.Username},
RoleEntity: &milvuspb.RoleEntity{Name: in.RoleName},
},
}).
WithBody(&message.AlterUserRoleMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
case milvuspb.OperateUserRoleType_RemoveUserFromRole:
msg = message.NewDropUserRoleMessageBuilderV2().
WithHeader(&message.DropUserRoleMessageHeader{
RoleBinding: &message.RoleBinding{
UserEntity: &milvuspb.UserEntity{Name: in.Username},
RoleEntity: &milvuspb.RoleEntity{Name: in.RoleName},
},
}).
WithBody(&message.DropUserRoleMessageBody{}).
WithBroadcast([]string{streaming.WAL().ControlChannel()}).
MustBuildBroadcast()
default:
return errors.New("invalid operate user role type")
}
_, err = broadcaster.Broadcast(ctx, msg)
return err
}
func (c *DDLCallback) alterUserRoleV2AckCallback(ctx context.Context, result message.BroadcastResultAlterUserRoleMessageV2) error {
header := result.Message.Header()
if err := c.meta.OperateUserRole(ctx, util.DefaultTenant, header.RoleBinding.UserEntity, header.RoleBinding.RoleEntity, milvuspb.OperateUserRoleType_AddUserToRole); err != nil {
return errors.Wrap(err, "failed to operate user role")
}
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheAddUserToRole),
OpKey: funcutil.EncodeUserRoleCache(header.RoleBinding.UserEntity.Name, header.RoleBinding.RoleEntity.Name),
}); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}
func (c *DDLCallback) dropUserRoleV2AckCallback(ctx context.Context, result message.BroadcastResultDropUserRoleMessageV2) error {
header := result.Message.Header()
if err := c.meta.OperateUserRole(ctx, util.DefaultTenant, header.RoleBinding.UserEntity, header.RoleBinding.RoleEntity, milvuspb.OperateUserRoleType_RemoveUserFromRole); err != nil {
return errors.Wrap(err, "failed to operate user role")
}
if err := c.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheRemoveUserFromRole),
OpKey: funcutil.EncodeUserRoleCache(header.RoleBinding.UserEntity.Name, header.RoleBinding.RoleEntity.Name),
}); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}

View File

@ -0,0 +1,155 @@
// Licensed to the LF AI & Data foundation under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rootcoord
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable"
)
func TestDDLCallbacksRBACRole(t *testing.T) {
initStreamingSystem()
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
core := newTestCore(withHealthyCode(),
withMeta(&MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}),
withValidProxyManager(),
)
registry.ResetRegistration()
RegisterDDLCallbacks(core)
// Test drop builtin role should return error
roleDbAdmin := "db_admin"
paramtable.Init()
paramtable.Get().Save(paramtable.Get().RoleCfg.Enabled.Key, "true")
paramtable.Get().Save(paramtable.Get().RoleCfg.Roles.Key, `{"`+roleDbAdmin+`": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}}`)
err := core.initBuiltinRoles(context.Background())
assert.Equal(t, nil, err)
assert.True(t, util.IsBuiltinRole(roleDbAdmin))
assert.False(t, util.IsBuiltinRole(util.RoleAdmin))
resp, err := core.DropRole(context.Background(), &milvuspb.DropRoleRequest{RoleName: roleDbAdmin})
assert.Equal(t, nil, err)
assert.Equal(t, int32(1401), resp.Code) // merr.ErrPrivilegeNotPermitted
// Create a new credential.
testUserName := "user" + funcutil.RandomString(10)
status, err := core.CreateCredential(context.Background(), &internalpb.CredentialInfo{
Username: testUserName,
EncryptedPassword: "123456",
})
require.NoError(t, merr.CheckRPCCall(status, err))
testRoleName := "role" + funcutil.RandomString(10)
// Drop a not existed role should return error.
status, err = core.DropRole(context.Background(), &milvuspb.DropRoleRequest{
RoleName: testRoleName,
})
require.Error(t, merr.CheckRPCCall(status, err))
// Operate a not existed role should return error.
status, err = core.OperateUserRole(context.Background(), &milvuspb.OperateUserRoleRequest{
RoleName: testRoleName,
Username: testUserName,
Type: milvuspb.OperateUserRoleType_AddUserToRole,
})
require.Error(t, merr.CheckRPCCall(status, err))
// Create a new role.
status, err = core.CreateRole(context.Background(), &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{
Name: testRoleName,
},
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectRoleResp, err := core.SelectRole(context.Background(), &milvuspb.SelectRoleRequest{
Role: &milvuspb.RoleEntity{
Name: testRoleName,
},
})
require.NoError(t, merr.CheckRPCCall(status, err))
assert.Equal(t, 1, len(selectRoleResp.Results))
assert.Equal(t, testRoleName, selectRoleResp.Results[0].Role.GetName())
// Add user to role.
status, err = core.OperateUserRole(context.Background(), &milvuspb.OperateUserRoleRequest{
RoleName: testRoleName,
Username: testUserName,
Type: milvuspb.OperateUserRoleType_AddUserToRole,
})
assert.NoError(t, merr.CheckRPCCall(status, err))
selectRoleResp, err = core.SelectRole(context.Background(), &milvuspb.SelectRoleRequest{
Role: &milvuspb.RoleEntity{
Name: testRoleName,
},
IncludeUserInfo: true,
})
require.NoError(t, merr.CheckRPCCall(status, err))
assert.Equal(t, 1, len(selectRoleResp.Results))
assert.Equal(t, testRoleName, selectRoleResp.Results[0].Role.GetName())
assert.Equal(t, 1, len(selectRoleResp.Results[0].Users))
assert.Equal(t, testUserName, selectRoleResp.Results[0].Users[0].GetName())
// Remove a user from role.
status, err = core.OperateUserRole(context.Background(), &milvuspb.OperateUserRoleRequest{
RoleName: testRoleName,
Username: testUserName,
Type: milvuspb.OperateUserRoleType_RemoveUserFromRole,
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectRoleResp, err = core.SelectRole(context.Background(), &milvuspb.SelectRoleRequest{
Role: &milvuspb.RoleEntity{
Name: testRoleName,
},
IncludeUserInfo: true,
})
require.NoError(t, merr.CheckRPCCall(status, err))
assert.Equal(t, 1, len(selectRoleResp.Results))
assert.Equal(t, testRoleName, selectRoleResp.Results[0].Role.GetName())
assert.Equal(t, 0, len(selectRoleResp.Results[0].Users))
// Drop a role with force drop.
status, err = core.DropRole(context.Background(), &milvuspb.DropRoleRequest{
RoleName: testRoleName,
ForceDrop: true,
})
require.NoError(t, merr.CheckRPCCall(status, err))
selectRoleResp, err = core.SelectRole(context.Background(), &milvuspb.SelectRoleRequest{
Role: &milvuspb.RoleEntity{
Name: testRoleName,
},
})
require.NoError(t, merr.CheckRPCCall(status, err))
assert.Equal(t, 0, len(selectRoleResp.Results))
}

View File

@ -0,0 +1,62 @@
package rootcoord
import (
"context"
"github.com/cockroachdb/errors"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
)
var (
errEmptyUsername = errors.New("username is empty")
errUserNotFound = errors.New("user not found")
errUserAlreadyExists = errors.New("user already exists")
errEmptyRoleName = errors.New("role name is empty")
errRoleAlreadyExists = errors.New("role already exists")
errRoleNotExists = errors.New("role not exists")
errEmptyRBACMeta = errors.New("rbac meta is empty")
errNotCustomPrivilegeGroup = errors.New("not a custom privilege group")
errEmptyPrivilegeGroupName = errors.New("privilege group name is empty")
)
type RBACChecker interface {
// CheckIfAddCredential checks if the credential can be added.
// if the credential already exists, it will return errUserAlreadyExists.
CheckIfAddCredential(ctx context.Context, req *internalpb.CredentialInfo) error
// CheckIfUpdateCredential checks if the credential can be updated.
// if the credential not exists, it will return errUserNotFound.
CheckIfUpdateCredential(ctx context.Context, req *internalpb.CredentialInfo) error
// CheckIfDeleteCredential checks if the credential can be deleted.
// if the credential not exists, it will return errUserNotFound.
CheckIfDeleteCredential(ctx context.Context, req *milvuspb.DeleteCredentialRequest) error
// CheckIfCreateRole checks if the role can be created.
// if the role already exists, it will return errRoleAlreadyExists.
CheckIfCreateRole(ctx context.Context, req *milvuspb.CreateRoleRequest) error
// CheckIfDropRole checks if the role can be dropped.
// if the role not exists, it will return errRoleNotExists.
CheckIfDropRole(ctx context.Context, in *milvuspb.DropRoleRequest) error
// CheckIfOperateUserRole checks if the user role can be operated.
CheckIfOperateUserRole(ctx context.Context, req *milvuspb.OperateUserRoleRequest) error
// CheckIfPrivilegeGroupCreatable checks if the privilege group can be created.
CheckIfPrivilegeGroupCreatable(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) error
// CheckIfPrivilegeGroupAlterable checks if the privilege group can be altered.
CheckIfPrivilegeGroupAlterable(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) error
// CheckIfPrivilegeGroupDropable checks if the privilege group can be dropped.
CheckIfPrivilegeGroupDropable(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) error
// CheckIfRBACRestorable checks if the rbac meta data can be restored.
CheckIfRBACRestorable(ctx context.Context, req *milvuspb.RestoreRBACMetaRequest) error
}

View File

@ -39,8 +39,10 @@ import (
pb "github.com/milvus-io/milvus/pkg/v2/proto/etcdpb"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/rootcoordpb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/contextutil"
"github.com/milvus-io/milvus/pkg/v2/util/crypto"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable"
@ -48,8 +50,14 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
type MetaTableChecker interface {
RBACChecker
}
//go:generate mockery --name=IMetaTable --structname=MockIMetaTable --output=./ --filename=mock_meta_table.go --with-expecter --inpackage
type IMetaTable interface {
MetaTableChecker
GetDatabaseByID(ctx context.Context, dbID int64, ts Timestamp) (*model.Database, error)
GetDatabaseByName(ctx context.Context, dbName string, ts Timestamp) (*model.Database, error)
CreateDatabase(ctx context.Context, db *model.Database, ts typeutil.Timestamp) error
@ -91,10 +99,10 @@ type IMetaTable interface {
IsAlias(ctx context.Context, db, name string) bool
ListAliasesByID(ctx context.Context, collID UniqueID) []string
AddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error
GetCredential(ctx context.Context, username string) (*internalpb.CredentialInfo, error)
DeleteCredential(ctx context.Context, username string) error
AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error
InitCredential(ctx context.Context) error
DeleteCredential(ctx context.Context, result message.BroadcastResultDropUserMessageV2) error
AlterCredential(ctx context.Context, result message.BroadcastResultAlterUserMessageV2) error
ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error)
CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error
@ -1353,47 +1361,103 @@ func (mt *MetaTable) GetGeneralCount(ctx context.Context) int {
return mt.generalCnt
}
// AddCredential add credential
func (mt *MetaTable) AddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
if credInfo.Username == "" {
return errors.New("username is empty")
}
func (mt *MetaTable) InitCredential(ctx context.Context) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
credInfo, err := mt.catalog.GetCredential(ctx, util.UserRoot)
if err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) {
return err
}
if credInfo != nil {
return nil
}
encryptedRootPassword, err := crypto.PasswordEncrypt(Params.CommonCfg.DefaultRootPassword.GetValue())
if err != nil {
log.Ctx(ctx).Warn("RootCoord init user root failed", zap.Error(err))
return err
}
log.Ctx(ctx).Info("RootCoord init user root")
err = mt.catalog.AlterCredential(ctx, &model.Credential{
Username: util.UserRoot,
EncryptedPassword: encryptedRootPassword,
})
if err != nil {
log.Ctx(ctx).Warn("RootCoord init user root failed", zap.Error(err))
return err
}
return nil
}
func (mt *MetaTable) CheckIfAddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
if funcutil.IsEmptyString(credInfo.GetUsername()) {
return errEmptyUsername
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
usernames, err := mt.catalog.ListCredentials(ctx)
if err != nil {
return err
}
if len(usernames) >= Params.ProxyCfg.MaxUserNum.GetAsInt() {
// check if the username already exists.
for _, username := range usernames {
if username == credInfo.GetUsername() {
return errUserAlreadyExists
}
}
// check if the number of users has reached the limit.
maxUserNum := Params.ProxyCfg.MaxUserNum.GetAsInt()
if len(usernames) >= maxUserNum {
errMsg := "unable to add user because the number of users has reached the limit"
log.Ctx(ctx).Error(errMsg, zap.Int("max_user_num", Params.ProxyCfg.MaxUserNum.GetAsInt()))
log.Ctx(ctx).Error(errMsg, zap.Int("maxUserNum", maxUserNum))
return errors.New(errMsg)
}
return nil
}
if origin, _ := mt.catalog.GetCredential(ctx, credInfo.Username); origin != nil {
return fmt.Errorf("user already exists: %s", credInfo.Username)
func (mt *MetaTable) CheckIfUpdateCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
if funcutil.IsEmptyString(credInfo.GetUsername()) {
return errEmptyUsername
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
credential := &model.Credential{
Username: credInfo.Username,
EncryptedPassword: credInfo.EncryptedPassword,
// check if the number of credential exists.
if _, err := mt.catalog.GetCredential(ctx, credInfo.GetUsername()); err != nil {
if errors.Is(err, merr.ErrIoKeyNotFound) {
return errUserNotFound
}
return err
}
return mt.catalog.CreateCredential(ctx, credential)
return nil
}
// AlterCredential update credential
func (mt *MetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
if credInfo.Username == "" {
return errors.New("username is empty")
}
func (mt *MetaTable) AlterCredential(ctx context.Context, result message.BroadcastResultAlterUserMessageV2) error {
body := result.Message.MustBody()
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
existsCredential, err := mt.catalog.GetCredential(ctx, body.CredentialInfo.Username)
if err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) {
return err
}
// if the credential already exists and the version is not greater than the current timetick.
if existsCredential != nil && existsCredential.TimeTick >= result.GetControlChannelResult().TimeTick {
log.Info("credential already exists and the version is not greater than the current timetick",
zap.String("username", body.CredentialInfo.Username),
zap.Uint64("incoming", result.GetControlChannelResult().TimeTick),
zap.Uint64("current", existsCredential.TimeTick),
)
return nil
}
credential := &model.Credential{
Username: credInfo.Username,
EncryptedPassword: credInfo.EncryptedPassword,
Username: body.CredentialInfo.Username,
EncryptedPassword: body.CredentialInfo.EncryptedPassword,
TimeTick: result.GetControlChannelResult().TimeTick,
}
return mt.catalog.AlterCredential(ctx, credential)
}
@ -1407,12 +1471,42 @@ func (mt *MetaTable) GetCredential(ctx context.Context, username string) (*inter
return model.MarshalCredentialModel(credential), err
}
func (mt *MetaTable) CheckIfDeleteCredential(ctx context.Context, req *milvuspb.DeleteCredentialRequest) error {
if funcutil.IsEmptyString(req.GetUsername()) {
return errEmptyUsername
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
// check if the number of credential exists.
if _, err := mt.catalog.GetCredential(ctx, req.GetUsername()); err != nil {
if errors.Is(err, merr.ErrIoKeyNotFound) {
return errUserNotFound
}
return err
}
return nil
}
// DeleteCredential delete credential
func (mt *MetaTable) DeleteCredential(ctx context.Context, username string) error {
func (mt *MetaTable) DeleteCredential(ctx context.Context, result message.BroadcastResultDropUserMessageV2) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
return mt.catalog.DropCredential(ctx, username)
existsCredential, err := mt.catalog.GetCredential(ctx, result.Message.Header().UserName)
if err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) {
return err
}
// if the credential already exists and the version is not greater than the current timetick.
if existsCredential != nil && existsCredential.TimeTick >= result.GetControlChannelResult().TimeTick {
log.Info("credential already exists and the version is not greater than the current timetick",
zap.String("username", result.Message.Header().UserName),
zap.Uint64("incoming", result.GetControlChannelResult().TimeTick),
zap.Uint64("current", existsCredential.TimeTick),
)
return nil
}
return mt.catalog.DropCredential(ctx, result.Message.Header().UserName)
}
// ListCredentialUsernames list credential usernames
@ -1427,23 +1521,23 @@ func (mt *MetaTable) ListCredentialUsernames(ctx context.Context) (*milvuspb.Lis
return &milvuspb.ListCredUsersResponse{Usernames: usernames}, nil
}
// CreateRole create role
func (mt *MetaTable) CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
if funcutil.IsEmptyString(entity.Name) {
return errors.New("the role name in the role info is empty")
// CheckIfCreateRole checks if the role can be created.
func (mt *MetaTable) CheckIfCreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest) error {
if funcutil.IsEmptyString(in.GetEntity().GetName()) {
return errEmptyRoleName
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
results, err := mt.catalog.ListRole(ctx, tenant, nil, false)
results, err := mt.catalog.ListRole(ctx, util.DefaultTenant, nil, false)
if err != nil {
log.Ctx(ctx).Warn("fail to list roles", zap.Error(err))
return err
}
for _, result := range results {
if result.GetRole().GetName() == entity.Name {
log.Ctx(ctx).Info("role already exists", zap.String("role", entity.Name))
return common.NewIgnorableError(errors.Newf("role [%s] already exists", entity))
if result.GetRole().GetName() == in.GetEntity().GetName() {
log.Ctx(ctx).Info("role already exists", zap.String("role", in.GetEntity().GetName()))
return errRoleAlreadyExists
}
}
if len(results) >= Params.ProxyCfg.MaxRoleNum.GetAsInt() {
@ -1451,10 +1545,51 @@ func (mt *MetaTable) CreateRole(ctx context.Context, tenant string, entity *milv
log.Ctx(ctx).Warn(errMsg, zap.Int("max_role_num", Params.ProxyCfg.MaxRoleNum.GetAsInt()))
return errors.New(errMsg)
}
return nil
}
// CreateRole create role
func (mt *MetaTable) CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
return mt.catalog.CreateRole(ctx, tenant, entity)
}
func (mt *MetaTable) CheckIfDropRole(ctx context.Context, in *milvuspb.DropRoleRequest) error {
if funcutil.IsEmptyString(in.GetRoleName()) {
return errEmptyRoleName
}
if util.IsBuiltinRole(in.GetRoleName()) {
return merr.WrapErrPrivilegeNotPermitted("the role[%s] is a builtin role, which can't be dropped", in.GetRoleName())
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
if _, err := mt.catalog.ListRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: in.GetRoleName()}, false); err != nil {
if errors.Is(err, merr.ErrIoKeyNotFound) {
return errRoleNotExists
}
return err
}
if in.GetForceDrop() {
return nil
}
grantEntities, err := mt.catalog.ListGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: in.GetRoleName()},
DbName: "*",
})
if err != nil {
return err
}
if len(grantEntities) != 0 {
errMsg := "fail to drop the role that it has privileges. Use REVOKE API to revoke privileges"
return errors.New(errMsg)
}
return nil
}
// DropRole drop role info
func (mt *MetaTable) DropRole(ctx context.Context, tenant string, roleName string) error {
mt.permissionLock.Lock()
@ -1463,15 +1598,33 @@ func (mt *MetaTable) DropRole(ctx context.Context, tenant string, roleName strin
return mt.catalog.DropRole(ctx, tenant, roleName)
}
// OperateUserRole operate the relationship between a user and a role, including adding a user to a role and removing a user from a role
func (mt *MetaTable) OperateUserRole(ctx context.Context, tenant string, userEntity *milvuspb.UserEntity, roleEntity *milvuspb.RoleEntity, operateType milvuspb.OperateUserRoleType) error {
if funcutil.IsEmptyString(userEntity.Name) {
func (mt *MetaTable) CheckIfOperateUserRole(ctx context.Context, req *milvuspb.OperateUserRoleRequest) error {
if funcutil.IsEmptyString(req.GetUsername()) {
return errors.New("username in the user entity is empty")
}
if funcutil.IsEmptyString(roleEntity.Name) {
if funcutil.IsEmptyString(req.GetRoleName()) {
return errors.New("role name in the role entity is empty")
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
if _, err := mt.catalog.ListRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: req.RoleName}, false); err != nil {
if errors.Is(err, merr.ErrIoKeyNotFound) {
return errRoleNotExists
}
return err
}
if req.Type != milvuspb.OperateUserRoleType_RemoveUserFromRole {
if _, err := mt.catalog.ListUser(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: req.Username}, false); err != nil {
errMsg := "not found the user, maybe the user isn't existed or internal system error"
return errors.New(errMsg)
}
}
return nil
}
// OperateUserRole operate the relationship between a user and a role, including adding a user to a role and removing a user from a role
func (mt *MetaTable) OperateUserRole(ctx context.Context, tenant string, userEntity *milvuspb.UserEntity, roleEntity *milvuspb.RoleEntity, operateType milvuspb.OperateUserRoleType) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
@ -1584,6 +1737,72 @@ func (mt *MetaTable) BackupRBAC(ctx context.Context, tenant string) (*milvuspb.R
return mt.catalog.BackupRBAC(ctx, tenant)
}
func (mt *MetaTable) CheckIfRBACRestorable(ctx context.Context, req *milvuspb.RestoreRBACMetaRequest) error {
meta := req.GetRBACMeta()
if len(meta.GetRoles()) == 0 && len(meta.GetPrivilegeGroups()) == 0 && len(meta.GetGrants()) == 0 && len(meta.GetUsers()) == 0 {
return errEmptyRBACMeta
}
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
// check if role already exists
existRoles, err := mt.catalog.ListRole(ctx, util.DefaultTenant, nil, false)
if err != nil {
return err
}
existRoleMap := lo.SliceToMap(existRoles, func(entity *milvuspb.RoleResult) (string, struct{}) { return entity.GetRole().GetName(), struct{}{} })
existRoleAfterRestoreMap := lo.SliceToMap(existRoles, func(entity *milvuspb.RoleResult) (string, struct{}) { return entity.GetRole().GetName(), struct{}{} })
for _, role := range meta.GetRoles() {
if _, ok := existRoleMap[role.GetName()]; ok {
return errors.Newf("role [%s] already exists", role.GetName())
}
existRoleAfterRestoreMap[role.GetName()] = struct{}{}
}
// check if privilege group already exists
existPrivGroups, err := mt.catalog.ListPrivilegeGroups(ctx)
if err != nil {
return err
}
existPrivGroupMap := lo.SliceToMap(existPrivGroups, func(entity *milvuspb.PrivilegeGroupInfo) (string, struct{}) { return entity.GetGroupName(), struct{}{} })
existPrivGroupAfterRestoreMap := lo.SliceToMap(existPrivGroups, func(entity *milvuspb.PrivilegeGroupInfo) (string, struct{}) { return entity.GetGroupName(), struct{}{} })
for _, group := range meta.GetPrivilegeGroups() {
if _, ok := existPrivGroupMap[group.GetGroupName()]; ok {
return errors.Newf("privilege group [%s] already exists", group.GetGroupName())
}
existPrivGroupAfterRestoreMap[group.GetGroupName()] = struct{}{}
}
// check if grant can be restored
for _, grant := range meta.GetGrants() {
privName := grant.GetGrantor().GetPrivilege().GetName()
if _, ok := existPrivGroupAfterRestoreMap[privName]; !ok && !util.IsPrivilegeNameDefined(privName) {
return errors.Newf("privilege [%s] does not exist", privName)
}
}
// check if user can be restored
existUser, err := mt.catalog.ListUser(ctx, util.DefaultTenant, nil, false)
if err != nil {
return err
}
existUserMap := lo.SliceToMap(existUser, func(entity *milvuspb.UserResult) (string, struct{}) { return entity.GetUser().GetName(), struct{}{} })
for _, user := range meta.GetUsers() {
if _, ok := existUserMap[user.GetUser()]; ok {
return errors.Newf("user [%s] already exists", user.GetUser())
}
// check if user-role can be restored
for _, role := range user.GetRoles() {
if _, ok := existRoleAfterRestoreMap[role.GetName()]; !ok {
return errors.Newf("role [%s] does not exist", role.GetName())
}
}
}
return nil
}
func (mt *MetaTable) RestoreRBAC(ctx context.Context, tenant string, meta *milvuspb.RBACMeta) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
@ -1605,23 +1824,30 @@ func (mt *MetaTable) IsCustomPrivilegeGroup(ctx context.Context, groupName strin
return false, nil
}
func (mt *MetaTable) CreatePrivilegeGroup(ctx context.Context, groupName string) error {
if funcutil.IsEmptyString(groupName) {
return errors.New("the privilege group name is empty")
func (mt *MetaTable) CheckIfPrivilegeGroupCreatable(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) error {
if funcutil.IsEmptyString(req.GetGroupName()) {
return errEmptyPrivilegeGroupName
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, groupName)
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, req.GetGroupName())
if err != nil {
return err
}
if definedByUsers {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by users", groupName)
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by users", req.GetGroupName())
}
if util.IsPrivilegeNameDefined(groupName) {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by built in privileges or privilege groups in system", groupName)
if util.IsPrivilegeNameDefined(req.GetGroupName()) {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by built in privileges or privilege groups in system", req.GetGroupName())
}
return nil
}
func (mt *MetaTable) CreatePrivilegeGroup(ctx context.Context, groupName string) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
data := &milvuspb.PrivilegeGroupInfo{
GroupName: groupName,
Privileges: make([]*milvuspb.PrivilegeEntity, 0),
@ -1629,20 +1855,21 @@ func (mt *MetaTable) CreatePrivilegeGroup(ctx context.Context, groupName string)
return mt.catalog.SavePrivilegeGroup(ctx, data)
}
func (mt *MetaTable) DropPrivilegeGroup(ctx context.Context, groupName string) error {
if funcutil.IsEmptyString(groupName) {
return errors.New("the privilege group name is empty")
func (mt *MetaTable) CheckIfPrivilegeGroupDropable(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) error {
if funcutil.IsEmptyString(req.GetGroupName()) {
return errEmptyPrivilegeGroupName
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, groupName)
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, req.GetGroupName())
if err != nil {
return err
}
if !definedByUsers {
return nil
return errNotCustomPrivilegeGroup
}
// check if the group is used by any role
roles, err := mt.catalog.ListRole(ctx, util.DefaultTenant, nil, false)
if err != nil {
@ -1660,11 +1887,18 @@ func (mt *MetaTable) DropPrivilegeGroup(ctx context.Context, groupName string) e
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())
if grant.Grantor.Privilege.Name == req.GetGroupName() {
return errors.Newf("privilege group [%s] is used by role [%s], Use REVOKE API to revoke it first", req.GetGroupName(), role.GetName())
}
}
}
return nil
}
func (mt *MetaTable) DropPrivilegeGroup(ctx context.Context, groupName string) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
return mt.catalog.DropPrivilegeGroup(ctx, groupName)
}
@ -1675,43 +1909,55 @@ func (mt *MetaTable) ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.Privi
return mt.catalog.ListPrivilegeGroups(ctx)
}
func (mt *MetaTable) OperatePrivilegeGroup(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
if funcutil.IsEmptyString(groupName) {
return errors.New("the privilege group name is empty")
// CheckIfPrivilegeGroupAlterable checks if the privilege group can be altered.
func (mt *MetaTable) CheckIfPrivilegeGroupAlterable(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) error {
if funcutil.IsEmptyString(req.GetGroupName()) {
return errEmptyPrivilegeGroupName
}
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
if util.IsBuiltinPrivilegeGroup(groupName) {
return merr.WrapErrParameterInvalidMsg("the privilege group name [%s] is defined by built in privilege groups in system", groupName)
}
// validate input params
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, 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(ctx)
if err != nil {
return err
}
for _, p := range privileges {
currenctGroups := lo.SliceToMap(groups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges
})
// check if the privilege group is defined by users
if _, ok := currenctGroups[req.GroupName]; !ok {
return merr.WrapErrParameterInvalidMsg("there is no privilege group name [%s] defined in system to operate", req.GroupName)
}
if len(req.Privileges) == 0 {
return merr.WrapErrParameterInvalidMsg("privileges is empty when alter the privilege group")
}
// check if the new incoming privileges are defined by users or built in
for _, p := range req.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 privilege group name [%s] defined in system to operate", p.Name)
}
if _, ok := currenctGroups[p.Name]; !ok {
return merr.WrapErrParameterInvalidMsg("there is no privilege name or privilege group name [%s] defined in system to operate", p.Name)
}
}
if req.Type == milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup {
// Check if all privileges are the same privilege level
privilegeLevels := lo.SliceToMap(lo.Union(req.Privileges, currenctGroups[req.GroupName]), func(p *milvuspb.PrivilegeEntity) (string, struct{}) {
return util.GetPrivilegeLevel(p.Name), struct{}{}
})
if len(privilegeLevels) > 1 {
return merr.WrapErrParameterInvalidMsg("privileges are not the same privilege level")
}
}
return nil
}
func (mt *MetaTable) OperatePrivilegeGroup(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error {
mt.permissionLock.Lock()
defer mt.permissionLock.Unlock()
// merge with current privileges
group, err := mt.catalog.GetPrivilegeGroup(ctx, groupName)
if err != nil {

View File

@ -28,31 +28,80 @@ import (
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
memkv "github.com/milvus-io/milvus/internal/kv/mem"
etcdkv "github.com/milvus-io/milvus/internal/kv/etcd"
"github.com/milvus-io/milvus/internal/metastore/kv/rootcoord"
"github.com/milvus-io/milvus/internal/metastore/mocks"
"github.com/milvus-io/milvus/internal/metastore/model"
"github.com/milvus-io/milvus/internal/streamingcoord/server/balancer/channel"
mocktso "github.com/milvus-io/milvus/internal/tso/mocks"
"github.com/milvus-io/milvus/pkg/v2/common"
kvfactory "github.com/milvus-io/milvus/internal/util/dependency/kv"
pb "github.com/milvus-io/milvus/pkg/v2/proto/etcdpb"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable"
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
func generateMetaTable(t *testing.T) *MetaTable {
return &MetaTable{catalog: rootcoord.NewCatalog(memkv.NewMemoryKV(), nil)}
func generateMetaTable(_ *testing.T) *MetaTable {
kv, _ := kvfactory.GetEtcdAndPath()
path := funcutil.RandomString(10)
catalogKV := etcdkv.NewEtcdKV(kv, path)
return &MetaTable{catalog: rootcoord.NewCatalog(catalogKV, nil)}
}
func TestRbacAddCredential(t *testing.T) {
func buildAlterUserMessage(credInfo *internalpb.CredentialInfo, timetick uint64) message.BroadcastResultAlterUserMessageV2 {
msg := message.NewAlterUserMessageBuilderV2().
WithHeader(&message.AlterUserMessageHeader{
UserEntity: &milvuspb.UserEntity{
Name: credInfo.Username,
},
}).
WithBody(&message.AlterUserMessageBody{
CredentialInfo: credInfo,
}).
WithBroadcast([]string{funcutil.GetControlChannel("by-dev-rootcoord-dml_1")}).
MustBuildBroadcast()
return message.BroadcastResultAlterUserMessageV2{
Message: message.MustAsBroadcastAlterUserMessageV2(msg),
Results: map[string]*message.AppendResult{
funcutil.GetControlChannel("by-dev-rootcoord-dml_1"): {TimeTick: timetick},
},
}
}
func buildDropUserMessage(credInfo *internalpb.CredentialInfo, timetick uint64) message.BroadcastResultDropUserMessageV2 {
msg := message.NewDropUserMessageBuilderV2().
WithHeader(&message.DropUserMessageHeader{
UserName: credInfo.Username,
}).
WithBody(&message.DropUserMessageBody{}).
WithBroadcast([]string{funcutil.GetControlChannel("by-dev-rootcoord-dml_1")}).
MustBuildBroadcast()
return message.BroadcastResultDropUserMessageV2{
Message: message.MustAsBroadcastDropUserMessageV2(msg),
Results: map[string]*message.AppendResult{
funcutil.GetControlChannel("by-dev-rootcoord-dml_1"): {TimeTick: timetick},
},
}
}
func TestRbacCredential(t *testing.T) {
mt := generateMetaTable(t)
err := mt.AddCredential(context.TODO(), &internalpb.CredentialInfo{
Username: "user1",
username := "user" + funcutil.RandomString(10)
credInfo := &internalpb.CredentialInfo{
Username: username,
Tenant: util.DefaultTenant,
})
}
err := mt.CheckIfAddCredential(context.TODO(), credInfo)
require.NoError(t, err)
err = mt.AlterCredential(context.TODO(), buildAlterUserMessage(credInfo, 1))
require.NoError(t, err)
// idempotency
err = mt.AlterCredential(context.TODO(), buildAlterUserMessage(credInfo, 1))
require.NoError(t, err)
tests := []struct {
@ -63,7 +112,7 @@ func TestRbacAddCredential(t *testing.T) {
}{
{"Empty username", false, &internalpb.CredentialInfo{Username: ""}},
{"exceed MaxUserNum", true, &internalpb.CredentialInfo{Username: "user3", Tenant: util.DefaultTenant}},
{"user exist", false, &internalpb.CredentialInfo{Username: "user1", Tenant: util.DefaultTenant}},
{"user exist", false, &internalpb.CredentialInfo{Username: username, Tenant: util.DefaultTenant}},
}
for _, test := range tests {
@ -74,10 +123,39 @@ func TestRbacAddCredential(t *testing.T) {
paramtable.Get().Save(Params.ProxyCfg.MaxUserNum.Key, "3")
}
defer paramtable.Get().Reset(Params.ProxyCfg.MaxUserNum.Key)
err := mt.AddCredential(context.TODO(), test.info)
err := mt.CheckIfAddCredential(context.TODO(), test.info)
assert.Error(t, err)
})
}
// should be ignored if timetick is too low.
err = mt.AlterCredential(context.TODO(), buildAlterUserMessage(credInfo, 0))
require.NoError(t, err)
newCred, err := mt.GetCredential(context.TODO(), credInfo.Username)
require.NoError(t, err)
assert.Equal(t, newCred.TimeTick, uint64(1))
err = mt.AlterCredential(context.TODO(), buildAlterUserMessage(credInfo, 2))
require.NoError(t, err)
newCred, err = mt.GetCredential(context.TODO(), credInfo.Username)
require.NoError(t, err)
assert.Equal(t, newCred.TimeTick, uint64(2))
// should be ignored if timetick is too low.
err = mt.DeleteCredential(context.TODO(), buildDropUserMessage(credInfo, 2))
require.NoError(t, err)
newCred, err = mt.GetCredential(context.TODO(), credInfo.Username)
require.NoError(t, err)
assert.Equal(t, newCred.TimeTick, uint64(2))
err = mt.DeleteCredential(context.TODO(), buildDropUserMessage(credInfo, 3))
require.NoError(t, err)
newCred, err = mt.GetCredential(context.TODO(), credInfo.Username)
require.ErrorIs(t, err, merr.ErrIoKeyNotFound)
require.Nil(t, newCred)
err = mt.DeleteCredential(context.TODO(), buildDropUserMessage(credInfo, 0))
require.NoError(t, err)
}
func TestRbacCreateRole(t *testing.T) {
@ -85,7 +163,11 @@ func TestRbacCreateRole(t *testing.T) {
paramtable.Get().Save(Params.ProxyCfg.MaxRoleNum.Key, "2")
defer paramtable.Get().Reset(Params.ProxyCfg.MaxRoleNum.Key)
err := mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role1"})
err := mt.CheckIfCreateRole(context.TODO(), &milvuspb.CreateRoleRequest{Entity: &milvuspb.RoleEntity{Name: "role1"}})
require.NoError(t, err)
err = mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role1"})
require.NoError(t, err)
err = mt.CheckIfCreateRole(context.TODO(), &milvuspb.CreateRoleRequest{Entity: &milvuspb.RoleEntity{Name: "role2"}})
require.NoError(t, err)
err = mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role2"})
require.NoError(t, err)
@ -101,14 +183,14 @@ func TestRbacCreateRole(t *testing.T) {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := mt.CreateRole(context.TODO(), util.DefaultTenant, test.inEntity)
err := mt.CheckIfCreateRole(context.TODO(), &milvuspb.CreateRoleRequest{Entity: test.inEntity})
assert.Error(t, err)
})
}
t.Run("role has existed", func(t *testing.T) {
err := mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role1"})
err := mt.CheckIfCreateRole(context.TODO(), &milvuspb.CreateRoleRequest{Entity: &milvuspb.RoleEntity{Name: "role1"}})
assert.Error(t, err)
assert.True(t, common.IsIgnorableError(err))
assert.True(t, errors.Is(err, errRoleAlreadyExists))
})
{
@ -120,7 +202,7 @@ func TestRbacCreateRole(t *testing.T) {
mock.Anything,
).Return(nil, errors.New("error mock list role"))
mockMt := &MetaTable{catalog: mockCata}
err := mockMt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role1"})
err := mockMt.CheckIfCreateRole(context.TODO(), &milvuspb.CreateRoleRequest{Entity: &milvuspb.RoleEntity{Name: "role1"}})
assert.Error(t, err)
}
}
@ -128,24 +210,21 @@ func TestRbacCreateRole(t *testing.T) {
func TestRbacDropRole(t *testing.T) {
mt := generateMetaTable(t)
err := mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role1"})
// drop a exist role
roleExist := "role" + funcutil.RandomString(10)
err := mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: roleExist})
require.NoError(t, err)
err = mt.CheckIfDropRole(context.TODO(), &milvuspb.DropRoleRequest{RoleName: roleExist})
require.NoError(t, err)
err = mt.DropRole(context.TODO(), util.DefaultTenant, roleExist)
require.NoError(t, err)
// idempotency
mt.DropRole(context.TODO(), util.DefaultTenant, roleExist)
require.NoError(t, err)
tests := []struct {
roleName string
description string
}{
{"role1", "drop role1"},
{"role_not_exists", "drop not exist role"},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := mt.DropRole(context.TODO(), util.DefaultTenant, test.roleName)
assert.NoError(t, err)
})
}
// drop a not exist role
err = mt.CheckIfDropRole(context.TODO(), &milvuspb.DropRoleRequest{RoleName: "role_not_exist"})
require.ErrorIs(t, err, errRoleNotExists)
}
func TestRbacOperateRole(t *testing.T) {
@ -169,7 +248,11 @@ func TestRbacOperateRole(t *testing.T) {
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
err := mt.OperateUserRole(context.TODO(), util.DefaultTenant, &milvuspb.UserEntity{Name: test.user}, &milvuspb.RoleEntity{Name: test.role}, test.oType)
err := mt.CheckIfOperateUserRole(context.TODO(), &milvuspb.OperateUserRoleRequest{
Username: test.user,
RoleName: test.role,
Type: test.oType,
})
assert.Error(t, err)
})
}
@ -191,7 +274,7 @@ func TestRbacSelect(t *testing.T) {
}
for user, rs := range userRoles {
err := mt.catalog.CreateCredential(context.TODO(), &model.Credential{
err := mt.catalog.AlterCredential(context.TODO(), &model.Credential{
Username: user,
Tenant: util.DefaultTenant,
})
@ -2226,23 +2309,55 @@ func TestMetaTable_PrivilegeGroup(t *testing.T) {
aliases: newNameDb(),
catalog: catalog,
}
err := mt.CreatePrivilegeGroup(context.TODO(), "pg1")
err := mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "pg1",
})
assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "")
err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "",
})
assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "Insert")
err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "Insert",
})
assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "pg2")
err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "pg2",
})
assert.NoError(t, err)
err = mt.DropPrivilegeGroup(context.TODO(), "")
err = mt.CreatePrivilegeGroup(context.TODO(), "pg1")
assert.NoError(t, err)
// idempotency
err = mt.CreatePrivilegeGroup(context.TODO(), "pg1")
assert.NoError(t, err)
err = mt.CheckIfPrivilegeGroupDropable(context.TODO(), &milvuspb.DropPrivilegeGroupRequest{
GroupName: "",
})
assert.Error(t, err)
err = mt.CheckIfPrivilegeGroupDropable(context.TODO(), &milvuspb.DropPrivilegeGroupRequest{
GroupName: "pg1",
})
assert.NoError(t, err)
err = mt.DropPrivilegeGroup(context.TODO(), "pg1")
assert.NoError(t, err)
err = mt.OperatePrivilegeGroup(context.TODO(), "", []*milvuspb.PrivilegeEntity{}, milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup)
err = mt.CheckIfPrivilegeGroupAlterable(context.TODO(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: "",
Privileges: []*milvuspb.PrivilegeEntity{},
Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup,
})
assert.Error(t, err)
err = mt.OperatePrivilegeGroup(context.TODO(), "ClusterReadOnly", []*milvuspb.PrivilegeEntity{}, milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup)
err = mt.CheckIfPrivilegeGroupAlterable(context.TODO(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: "ClusterReadOnly",
Privileges: []*milvuspb.PrivilegeEntity{},
Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup,
})
assert.Error(t, err)
err = mt.OperatePrivilegeGroup(context.TODO(), "pg3", []*milvuspb.PrivilegeEntity{}, milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup)
err = mt.CheckIfPrivilegeGroupAlterable(context.TODO(), &milvuspb.OperatePrivilegeGroupRequest{
GroupName: "pg3",
Privileges: []*milvuspb.PrivilegeEntity{},
Type: milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup,
})
assert.Error(t, err)
_, err = mt.GetPrivilegeGroupRoles(context.TODO(), "")
assert.Error(t, err)

View File

@ -44,6 +44,7 @@ import (
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb"
"github.com/milvus-io/milvus/pkg/v2/proto/querypb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
"github.com/milvus-io/milvus/pkg/v2/util/metricsinfo"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable"
@ -56,6 +57,8 @@ const (
TestRootCoordID = 200
)
var _ IMetaTable = &mockMetaTable{}
// TODO: remove mockMetaTable, use mockery instead
type mockMetaTable struct {
IMetaTable
@ -83,8 +86,8 @@ type mockMetaTable struct {
RenameCollectionFunc func(ctx context.Context, oldName string, newName string, ts Timestamp) error
AddCredentialFunc func(ctx context.Context, credInfo *internalpb.CredentialInfo) error
GetCredentialFunc func(ctx context.Context, username string) (*internalpb.CredentialInfo, error)
DeleteCredentialFunc func(ctx context.Context, username string) error
AlterCredentialFunc func(ctx context.Context, credInfo *internalpb.CredentialInfo) error
DeleteCredentialFunc func(ctx context.Context, msg message.BroadcastResultDropUserMessageV2) error
AlterCredentialFunc func(ctx context.Context, msg message.BroadcastResultAlterUserMessageV2) error
ListCredentialUsernamesFunc func(ctx context.Context) (*milvuspb.ListCredUsersResponse, error)
CreateRoleFunc func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error
DropRoleFunc func(ctx context.Context, tenant string, roleName string) error
@ -205,12 +208,12 @@ func (m mockMetaTable) GetCredential(ctx context.Context, username string) (*int
return m.GetCredentialFunc(ctx, username)
}
func (m mockMetaTable) DeleteCredential(ctx context.Context, username string) error {
return m.DeleteCredentialFunc(ctx, username)
func (m mockMetaTable) DeleteCredential(ctx context.Context, msg message.BroadcastResultDropUserMessageV2) error {
return m.DeleteCredentialFunc(ctx, msg)
}
func (m mockMetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
return m.AlterCredentialFunc(ctx, credInfo)
func (m mockMetaTable) AlterCredential(ctx context.Context, msg message.BroadcastResultAlterUserMessageV2) error {
return m.AlterCredentialFunc(ctx, msg)
}
func (m mockMetaTable) ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) {
@ -373,12 +376,17 @@ func newMockTsoAllocator() *tso.MockAllocator {
type mockProxy struct {
types.ProxyClient
UpdateCredentialCacheFunc func(ctx context.Context, request *proxypb.UpdateCredCacheRequest) (*commonpb.Status, error)
InvalidateCollectionMetaCacheFunc func(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest) (*commonpb.Status, error)
InvalidateCredentialCacheFunc func(ctx context.Context, request *proxypb.InvalidateCredCacheRequest) (*commonpb.Status, error)
RefreshPolicyInfoCacheFunc func(ctx context.Context, request *proxypb.RefreshPolicyInfoCacheRequest) (*commonpb.Status, error)
GetComponentStatesFunc func(ctx context.Context) (*milvuspb.ComponentStates, error)
}
func (m mockProxy) UpdateCredentialCache(ctx context.Context, request *proxypb.UpdateCredCacheRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return m.UpdateCredentialCacheFunc(ctx, request)
}
func (m mockProxy) InvalidateCollectionMetaCache(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return m.InvalidateCollectionMetaCacheFunc(ctx, request)
}
@ -426,9 +434,18 @@ func withValidProxyManager() Opt {
return func(c *Core) {
c.proxyClientManager = proxyutil.NewProxyClientManager(proxyutil.DefaultProxyCreator)
p := newMockProxy()
p.UpdateCredentialCacheFunc = func(ctx context.Context, request *proxypb.UpdateCredCacheRequest) (*commonpb.Status, error) {
return merr.Success(), nil
}
p.InvalidateCredentialCacheFunc = func(ctx context.Context, request *proxypb.InvalidateCredCacheRequest) (*commonpb.Status, error) {
return merr.Success(), nil
}
p.InvalidateCollectionMetaCacheFunc = func(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest) (*commonpb.Status, error) {
return merr.Success(), nil
}
p.RefreshPolicyInfoCacheFunc = func(ctx context.Context, request *proxypb.RefreshPolicyInfoCacheRequest) (*commonpb.Status, error) {
return merr.Success(), nil
}
p.GetComponentStatesFunc = func(ctx context.Context) (*milvuspb.ComponentStates, error) {
return &milvuspb.ComponentStates{
State: &milvuspb.ComponentInfo{StateCode: commonpb.StateCode_Healthy},
@ -509,10 +526,10 @@ func withInvalidMeta() Opt {
meta.GetCredentialFunc = func(ctx context.Context, username string) (*internalpb.CredentialInfo, error) {
return nil, errors.New("error mock GetCredential")
}
meta.DeleteCredentialFunc = func(ctx context.Context, username string) error {
meta.DeleteCredentialFunc = func(ctx context.Context, msg message.BroadcastResultDropUserMessageV2) error {
return errors.New("error mock DeleteCredential")
}
meta.AlterCredentialFunc = func(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
meta.AlterCredentialFunc = func(ctx context.Context, msg message.BroadcastResultAlterUserMessageV2) error {
return errors.New("error mock AlterCredential")
}
meta.ListCredentialUsernamesFunc = func(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) {

View File

@ -8,6 +8,10 @@ import (
etcdpb "github.com/milvus-io/milvus/pkg/v2/proto/etcdpb"
internalpb "github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
message "github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
messagespb "github.com/milvus-io/milvus/pkg/v2/proto/messagespb"
milvuspb "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
mock "github.com/stretchr/testify/mock"
@ -77,53 +81,6 @@ func (_c *IMetaTable_AddCollection_Call) RunAndReturn(run func(context.Context,
return _c
}
// AddCredential provides a mock function with given fields: ctx, credInfo
func (_m *IMetaTable) AddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
ret := _m.Called(ctx, credInfo)
if len(ret) == 0 {
panic("no return value specified for AddCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *internalpb.CredentialInfo) error); ok {
r0 = rf(ctx, credInfo)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_AddCredential_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'AddCredential'
type IMetaTable_AddCredential_Call struct {
*mock.Call
}
// AddCredential is a helper method to define mock.On call
// - ctx context.Context
// - credInfo *internalpb.CredentialInfo
func (_e *IMetaTable_Expecter) AddCredential(ctx interface{}, credInfo interface{}) *IMetaTable_AddCredential_Call {
return &IMetaTable_AddCredential_Call{Call: _e.mock.On("AddCredential", ctx, credInfo)}
}
func (_c *IMetaTable_AddCredential_Call) Run(run func(ctx context.Context, credInfo *internalpb.CredentialInfo)) *IMetaTable_AddCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*internalpb.CredentialInfo))
})
return _c
}
func (_c *IMetaTable_AddCredential_Call) Return(_a0 error) *IMetaTable_AddCredential_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_AddCredential_Call) RunAndReturn(run func(context.Context, *internalpb.CredentialInfo) error) *IMetaTable_AddCredential_Call {
_c.Call.Return(run)
return _c
}
// AddPartition provides a mock function with given fields: ctx, partition
func (_m *IMetaTable) AddPartition(ctx context.Context, partition *model.Partition) error {
ret := _m.Called(ctx, partition)
@ -271,17 +228,17 @@ func (_c *IMetaTable_AlterCollection_Call) RunAndReturn(run func(context.Context
return _c
}
// AlterCredential provides a mock function with given fields: ctx, credInfo
func (_m *IMetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
ret := _m.Called(ctx, credInfo)
// AlterCredential provides a mock function with given fields: ctx, result
func (_m *IMetaTable) AlterCredential(ctx context.Context, result message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]) error {
ret := _m.Called(ctx, result)
if len(ret) == 0 {
panic("no return value specified for AlterCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *internalpb.CredentialInfo) error); ok {
r0 = rf(ctx, credInfo)
if rf, ok := ret.Get(0).(func(context.Context, message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]) error); ok {
r0 = rf(ctx, result)
} else {
r0 = ret.Error(0)
}
@ -296,14 +253,14 @@ type IMetaTable_AlterCredential_Call struct {
// AlterCredential is a helper method to define mock.On call
// - ctx context.Context
// - credInfo *internalpb.CredentialInfo
func (_e *IMetaTable_Expecter) AlterCredential(ctx interface{}, credInfo interface{}) *IMetaTable_AlterCredential_Call {
return &IMetaTable_AlterCredential_Call{Call: _e.mock.On("AlterCredential", ctx, credInfo)}
// - result message.BroadcastResult[*messagespb.AlterUserMessageHeader,*messagespb.AlterUserMessageBody]
func (_e *IMetaTable_Expecter) AlterCredential(ctx interface{}, result interface{}) *IMetaTable_AlterCredential_Call {
return &IMetaTable_AlterCredential_Call{Call: _e.mock.On("AlterCredential", ctx, result)}
}
func (_c *IMetaTable_AlterCredential_Call) Run(run func(ctx context.Context, credInfo *internalpb.CredentialInfo)) *IMetaTable_AlterCredential_Call {
func (_c *IMetaTable_AlterCredential_Call) Run(run func(ctx context.Context, result message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody])) *IMetaTable_AlterCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*internalpb.CredentialInfo))
run(args[0].(context.Context), args[1].(message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]))
})
return _c
}
@ -313,7 +270,7 @@ func (_c *IMetaTable_AlterCredential_Call) Return(_a0 error) *IMetaTable_AlterCr
return _c
}
func (_c *IMetaTable_AlterCredential_Call) RunAndReturn(run func(context.Context, *internalpb.CredentialInfo) error) *IMetaTable_AlterCredential_Call {
func (_c *IMetaTable_AlterCredential_Call) RunAndReturn(run func(context.Context, message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]) error) *IMetaTable_AlterCredential_Call {
_c.Call.Return(run)
return _c
}
@ -525,6 +482,476 @@ func (_c *IMetaTable_ChangePartitionState_Call) RunAndReturn(run func(context.Co
return _c
}
// CheckIfAddCredential provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfAddCredential(ctx context.Context, req *internalpb.CredentialInfo) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfAddCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *internalpb.CredentialInfo) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfAddCredential_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfAddCredential'
type IMetaTable_CheckIfAddCredential_Call struct {
*mock.Call
}
// CheckIfAddCredential is a helper method to define mock.On call
// - ctx context.Context
// - req *internalpb.CredentialInfo
func (_e *IMetaTable_Expecter) CheckIfAddCredential(ctx interface{}, req interface{}) *IMetaTable_CheckIfAddCredential_Call {
return &IMetaTable_CheckIfAddCredential_Call{Call: _e.mock.On("CheckIfAddCredential", ctx, req)}
}
func (_c *IMetaTable_CheckIfAddCredential_Call) Run(run func(ctx context.Context, req *internalpb.CredentialInfo)) *IMetaTable_CheckIfAddCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*internalpb.CredentialInfo))
})
return _c
}
func (_c *IMetaTable_CheckIfAddCredential_Call) Return(_a0 error) *IMetaTable_CheckIfAddCredential_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfAddCredential_Call) RunAndReturn(run func(context.Context, *internalpb.CredentialInfo) error) *IMetaTable_CheckIfAddCredential_Call {
_c.Call.Return(run)
return _c
}
// CheckIfCreateRole provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfCreateRole(ctx context.Context, req *milvuspb.CreateRoleRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfCreateRole")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreateRoleRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfCreateRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfCreateRole'
type IMetaTable_CheckIfCreateRole_Call struct {
*mock.Call
}
// CheckIfCreateRole is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.CreateRoleRequest
func (_e *IMetaTable_Expecter) CheckIfCreateRole(ctx interface{}, req interface{}) *IMetaTable_CheckIfCreateRole_Call {
return &IMetaTable_CheckIfCreateRole_Call{Call: _e.mock.On("CheckIfCreateRole", ctx, req)}
}
func (_c *IMetaTable_CheckIfCreateRole_Call) Run(run func(ctx context.Context, req *milvuspb.CreateRoleRequest)) *IMetaTable_CheckIfCreateRole_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.CreateRoleRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfCreateRole_Call) Return(_a0 error) *IMetaTable_CheckIfCreateRole_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfCreateRole_Call) RunAndReturn(run func(context.Context, *milvuspb.CreateRoleRequest) error) *IMetaTable_CheckIfCreateRole_Call {
_c.Call.Return(run)
return _c
}
// CheckIfDeleteCredential provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfDeleteCredential(ctx context.Context, req *milvuspb.DeleteCredentialRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfDeleteCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DeleteCredentialRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfDeleteCredential_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfDeleteCredential'
type IMetaTable_CheckIfDeleteCredential_Call struct {
*mock.Call
}
// CheckIfDeleteCredential is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.DeleteCredentialRequest
func (_e *IMetaTable_Expecter) CheckIfDeleteCredential(ctx interface{}, req interface{}) *IMetaTable_CheckIfDeleteCredential_Call {
return &IMetaTable_CheckIfDeleteCredential_Call{Call: _e.mock.On("CheckIfDeleteCredential", ctx, req)}
}
func (_c *IMetaTable_CheckIfDeleteCredential_Call) Run(run func(ctx context.Context, req *milvuspb.DeleteCredentialRequest)) *IMetaTable_CheckIfDeleteCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.DeleteCredentialRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfDeleteCredential_Call) Return(_a0 error) *IMetaTable_CheckIfDeleteCredential_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfDeleteCredential_Call) RunAndReturn(run func(context.Context, *milvuspb.DeleteCredentialRequest) error) *IMetaTable_CheckIfDeleteCredential_Call {
_c.Call.Return(run)
return _c
}
// CheckIfDropRole provides a mock function with given fields: ctx, in
func (_m *IMetaTable) CheckIfDropRole(ctx context.Context, in *milvuspb.DropRoleRequest) error {
ret := _m.Called(ctx, in)
if len(ret) == 0 {
panic("no return value specified for CheckIfDropRole")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropRoleRequest) error); ok {
r0 = rf(ctx, in)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfDropRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfDropRole'
type IMetaTable_CheckIfDropRole_Call struct {
*mock.Call
}
// CheckIfDropRole is a helper method to define mock.On call
// - ctx context.Context
// - in *milvuspb.DropRoleRequest
func (_e *IMetaTable_Expecter) CheckIfDropRole(ctx interface{}, in interface{}) *IMetaTable_CheckIfDropRole_Call {
return &IMetaTable_CheckIfDropRole_Call{Call: _e.mock.On("CheckIfDropRole", ctx, in)}
}
func (_c *IMetaTable_CheckIfDropRole_Call) Run(run func(ctx context.Context, in *milvuspb.DropRoleRequest)) *IMetaTable_CheckIfDropRole_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.DropRoleRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfDropRole_Call) Return(_a0 error) *IMetaTable_CheckIfDropRole_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfDropRole_Call) RunAndReturn(run func(context.Context, *milvuspb.DropRoleRequest) error) *IMetaTable_CheckIfDropRole_Call {
_c.Call.Return(run)
return _c
}
// CheckIfOperateUserRole provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfOperateUserRole(ctx context.Context, req *milvuspb.OperateUserRoleRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfOperateUserRole")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperateUserRoleRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfOperateUserRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfOperateUserRole'
type IMetaTable_CheckIfOperateUserRole_Call struct {
*mock.Call
}
// CheckIfOperateUserRole is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.OperateUserRoleRequest
func (_e *IMetaTable_Expecter) CheckIfOperateUserRole(ctx interface{}, req interface{}) *IMetaTable_CheckIfOperateUserRole_Call {
return &IMetaTable_CheckIfOperateUserRole_Call{Call: _e.mock.On("CheckIfOperateUserRole", ctx, req)}
}
func (_c *IMetaTable_CheckIfOperateUserRole_Call) Run(run func(ctx context.Context, req *milvuspb.OperateUserRoleRequest)) *IMetaTable_CheckIfOperateUserRole_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.OperateUserRoleRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfOperateUserRole_Call) Return(_a0 error) *IMetaTable_CheckIfOperateUserRole_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfOperateUserRole_Call) RunAndReturn(run func(context.Context, *milvuspb.OperateUserRoleRequest) error) *IMetaTable_CheckIfOperateUserRole_Call {
_c.Call.Return(run)
return _c
}
// CheckIfPrivilegeGroupAlterable provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfPrivilegeGroupAlterable(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfPrivilegeGroupAlterable")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfPrivilegeGroupAlterable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfPrivilegeGroupAlterable'
type IMetaTable_CheckIfPrivilegeGroupAlterable_Call struct {
*mock.Call
}
// CheckIfPrivilegeGroupAlterable is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.OperatePrivilegeGroupRequest
func (_e *IMetaTable_Expecter) CheckIfPrivilegeGroupAlterable(ctx interface{}, req interface{}) *IMetaTable_CheckIfPrivilegeGroupAlterable_Call {
return &IMetaTable_CheckIfPrivilegeGroupAlterable_Call{Call: _e.mock.On("CheckIfPrivilegeGroupAlterable", ctx, req)}
}
func (_c *IMetaTable_CheckIfPrivilegeGroupAlterable_Call) Run(run func(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest)) *IMetaTable_CheckIfPrivilegeGroupAlterable_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.OperatePrivilegeGroupRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupAlterable_Call) Return(_a0 error) *IMetaTable_CheckIfPrivilegeGroupAlterable_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupAlterable_Call) RunAndReturn(run func(context.Context, *milvuspb.OperatePrivilegeGroupRequest) error) *IMetaTable_CheckIfPrivilegeGroupAlterable_Call {
_c.Call.Return(run)
return _c
}
// CheckIfPrivilegeGroupCreatable provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfPrivilegeGroupCreatable(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfPrivilegeGroupCreatable")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfPrivilegeGroupCreatable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfPrivilegeGroupCreatable'
type IMetaTable_CheckIfPrivilegeGroupCreatable_Call struct {
*mock.Call
}
// CheckIfPrivilegeGroupCreatable is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.CreatePrivilegeGroupRequest
func (_e *IMetaTable_Expecter) CheckIfPrivilegeGroupCreatable(ctx interface{}, req interface{}) *IMetaTable_CheckIfPrivilegeGroupCreatable_Call {
return &IMetaTable_CheckIfPrivilegeGroupCreatable_Call{Call: _e.mock.On("CheckIfPrivilegeGroupCreatable", ctx, req)}
}
func (_c *IMetaTable_CheckIfPrivilegeGroupCreatable_Call) Run(run func(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest)) *IMetaTable_CheckIfPrivilegeGroupCreatable_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.CreatePrivilegeGroupRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupCreatable_Call) Return(_a0 error) *IMetaTable_CheckIfPrivilegeGroupCreatable_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupCreatable_Call) RunAndReturn(run func(context.Context, *milvuspb.CreatePrivilegeGroupRequest) error) *IMetaTable_CheckIfPrivilegeGroupCreatable_Call {
_c.Call.Return(run)
return _c
}
// CheckIfPrivilegeGroupDropable provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfPrivilegeGroupDropable(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfPrivilegeGroupDropable")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.DropPrivilegeGroupRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfPrivilegeGroupDropable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfPrivilegeGroupDropable'
type IMetaTable_CheckIfPrivilegeGroupDropable_Call struct {
*mock.Call
}
// CheckIfPrivilegeGroupDropable is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.DropPrivilegeGroupRequest
func (_e *IMetaTable_Expecter) CheckIfPrivilegeGroupDropable(ctx interface{}, req interface{}) *IMetaTable_CheckIfPrivilegeGroupDropable_Call {
return &IMetaTable_CheckIfPrivilegeGroupDropable_Call{Call: _e.mock.On("CheckIfPrivilegeGroupDropable", ctx, req)}
}
func (_c *IMetaTable_CheckIfPrivilegeGroupDropable_Call) Run(run func(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest)) *IMetaTable_CheckIfPrivilegeGroupDropable_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.DropPrivilegeGroupRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupDropable_Call) Return(_a0 error) *IMetaTable_CheckIfPrivilegeGroupDropable_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfPrivilegeGroupDropable_Call) RunAndReturn(run func(context.Context, *milvuspb.DropPrivilegeGroupRequest) error) *IMetaTable_CheckIfPrivilegeGroupDropable_Call {
_c.Call.Return(run)
return _c
}
// CheckIfRBACRestorable provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfRBACRestorable(ctx context.Context, req *milvuspb.RestoreRBACMetaRequest) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfRBACRestorable")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *milvuspb.RestoreRBACMetaRequest) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfRBACRestorable_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfRBACRestorable'
type IMetaTable_CheckIfRBACRestorable_Call struct {
*mock.Call
}
// CheckIfRBACRestorable is a helper method to define mock.On call
// - ctx context.Context
// - req *milvuspb.RestoreRBACMetaRequest
func (_e *IMetaTable_Expecter) CheckIfRBACRestorable(ctx interface{}, req interface{}) *IMetaTable_CheckIfRBACRestorable_Call {
return &IMetaTable_CheckIfRBACRestorable_Call{Call: _e.mock.On("CheckIfRBACRestorable", ctx, req)}
}
func (_c *IMetaTable_CheckIfRBACRestorable_Call) Run(run func(ctx context.Context, req *milvuspb.RestoreRBACMetaRequest)) *IMetaTable_CheckIfRBACRestorable_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*milvuspb.RestoreRBACMetaRequest))
})
return _c
}
func (_c *IMetaTable_CheckIfRBACRestorable_Call) Return(_a0 error) *IMetaTable_CheckIfRBACRestorable_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfRBACRestorable_Call) RunAndReturn(run func(context.Context, *milvuspb.RestoreRBACMetaRequest) error) *IMetaTable_CheckIfRBACRestorable_Call {
_c.Call.Return(run)
return _c
}
// CheckIfUpdateCredential provides a mock function with given fields: ctx, req
func (_m *IMetaTable) CheckIfUpdateCredential(ctx context.Context, req *internalpb.CredentialInfo) error {
ret := _m.Called(ctx, req)
if len(ret) == 0 {
panic("no return value specified for CheckIfUpdateCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *internalpb.CredentialInfo) error); ok {
r0 = rf(ctx, req)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_CheckIfUpdateCredential_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CheckIfUpdateCredential'
type IMetaTable_CheckIfUpdateCredential_Call struct {
*mock.Call
}
// CheckIfUpdateCredential is a helper method to define mock.On call
// - ctx context.Context
// - req *internalpb.CredentialInfo
func (_e *IMetaTable_Expecter) CheckIfUpdateCredential(ctx interface{}, req interface{}) *IMetaTable_CheckIfUpdateCredential_Call {
return &IMetaTable_CheckIfUpdateCredential_Call{Call: _e.mock.On("CheckIfUpdateCredential", ctx, req)}
}
func (_c *IMetaTable_CheckIfUpdateCredential_Call) Run(run func(ctx context.Context, req *internalpb.CredentialInfo)) *IMetaTable_CheckIfUpdateCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*internalpb.CredentialInfo))
})
return _c
}
func (_c *IMetaTable_CheckIfUpdateCredential_Call) Return(_a0 error) *IMetaTable_CheckIfUpdateCredential_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_CheckIfUpdateCredential_Call) RunAndReturn(run func(context.Context, *internalpb.CredentialInfo) error) *IMetaTable_CheckIfUpdateCredential_Call {
_c.Call.Return(run)
return _c
}
// CreateAlias provides a mock function with given fields: ctx, dbName, alias, collectionName, ts
func (_m *IMetaTable) CreateAlias(ctx context.Context, dbName string, alias string, collectionName string, ts uint64) error {
ret := _m.Called(ctx, dbName, alias, collectionName, ts)
@ -718,17 +1145,17 @@ func (_c *IMetaTable_CreateRole_Call) RunAndReturn(run func(context.Context, str
return _c
}
// DeleteCredential provides a mock function with given fields: ctx, username
func (_m *IMetaTable) DeleteCredential(ctx context.Context, username string) error {
ret := _m.Called(ctx, username)
// DeleteCredential provides a mock function with given fields: ctx, result
func (_m *IMetaTable) DeleteCredential(ctx context.Context, result message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]) error {
ret := _m.Called(ctx, result)
if len(ret) == 0 {
panic("no return value specified for DeleteCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, username)
if rf, ok := ret.Get(0).(func(context.Context, message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]) error); ok {
r0 = rf(ctx, result)
} else {
r0 = ret.Error(0)
}
@ -743,14 +1170,14 @@ type IMetaTable_DeleteCredential_Call struct {
// DeleteCredential is a helper method to define mock.On call
// - ctx context.Context
// - username string
func (_e *IMetaTable_Expecter) DeleteCredential(ctx interface{}, username interface{}) *IMetaTable_DeleteCredential_Call {
return &IMetaTable_DeleteCredential_Call{Call: _e.mock.On("DeleteCredential", ctx, username)}
// - result message.BroadcastResult[*messagespb.DropUserMessageHeader,*messagespb.DropUserMessageBody]
func (_e *IMetaTable_Expecter) DeleteCredential(ctx interface{}, result interface{}) *IMetaTable_DeleteCredential_Call {
return &IMetaTable_DeleteCredential_Call{Call: _e.mock.On("DeleteCredential", ctx, result)}
}
func (_c *IMetaTable_DeleteCredential_Call) Run(run func(ctx context.Context, username string)) *IMetaTable_DeleteCredential_Call {
func (_c *IMetaTable_DeleteCredential_Call) Run(run func(ctx context.Context, result message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody])) *IMetaTable_DeleteCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
run(args[0].(context.Context), args[1].(message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]))
})
return _c
}
@ -760,7 +1187,7 @@ func (_c *IMetaTable_DeleteCredential_Call) Return(_a0 error) *IMetaTable_Delete
return _c
}
func (_c *IMetaTable_DeleteCredential_Call) RunAndReturn(run func(context.Context, string) error) *IMetaTable_DeleteCredential_Call {
func (_c *IMetaTable_DeleteCredential_Call) RunAndReturn(run func(context.Context, message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]) error) *IMetaTable_DeleteCredential_Call {
_c.Call.Return(run)
return _c
}
@ -1676,6 +2103,52 @@ func (_c *IMetaTable_GetPrivilegeGroupRoles_Call) RunAndReturn(run func(context.
return _c
}
// InitCredential provides a mock function with given fields: ctx
func (_m *IMetaTable) InitCredential(ctx context.Context) error {
ret := _m.Called(ctx)
if len(ret) == 0 {
panic("no return value specified for InitCredential")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(ctx)
} else {
r0 = ret.Error(0)
}
return r0
}
// IMetaTable_InitCredential_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'InitCredential'
type IMetaTable_InitCredential_Call struct {
*mock.Call
}
// InitCredential is a helper method to define mock.On call
// - ctx context.Context
func (_e *IMetaTable_Expecter) InitCredential(ctx interface{}) *IMetaTable_InitCredential_Call {
return &IMetaTable_InitCredential_Call{Call: _e.mock.On("InitCredential", ctx)}
}
func (_c *IMetaTable_InitCredential_Call) Run(run func(ctx context.Context)) *IMetaTable_InitCredential_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *IMetaTable_InitCredential_Call) Return(_a0 error) *IMetaTable_InitCredential_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *IMetaTable_InitCredential_Call) RunAndReturn(run func(context.Context) error) *IMetaTable_InitCredential_Call {
_c.Call.Return(run)
return _c
}
// IsAlias provides a mock function with given fields: ctx, db, name
func (_m *IMetaTable) IsAlias(ctx context.Context, db string, name string) bool {
ret := _m.Called(ctx, db, name)

View File

@ -35,171 +35,64 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util/typeutil"
)
func executeDeleteCredentialTaskSteps(ctx context.Context, core *Core, username string) error {
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("delete credential meta data", func(ctx context.Context) ([]nestedStep, error) {
err := core.meta.DeleteCredential(ctx, username)
if err != nil {
log.Ctx(ctx).Warn("delete credential meta data failed", zap.String("username", username), zap.Error(err))
}
return nil, err
}))
redoTask.AddAsyncStep(NewSimpleStep("delete credential cache", func(ctx context.Context) ([]nestedStep, error) {
err := core.ExpireCredCache(ctx, username)
if err != nil {
log.Ctx(ctx).Warn("delete credential cache failed", zap.String("username", username), zap.Error(err))
}
return nil, err
}))
redoTask.AddAsyncStep(NewSimpleStep("delete user role cache for the user", func(ctx context.Context) ([]nestedStep, error) {
err := core.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheDeleteUser),
OpKey: username,
})
if err != nil {
log.Ctx(ctx).Warn("delete user role cache failed for the user", zap.String("username", username), zap.Error(err))
}
return nil, err
}))
return redoTask.Execute(ctx)
}
func executeDropRoleTaskSteps(ctx context.Context, core *Core, roleName string, foreDrop bool) error {
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("drop role meta data", func(ctx context.Context) ([]nestedStep, error) {
err := core.meta.DropRole(ctx, util.DefaultTenant, roleName)
if err != nil {
log.Ctx(ctx).Warn("drop role mata data failed", zap.String("role_name", roleName), zap.Error(err))
}
return nil, err
}))
redoTask.AddAsyncStep(NewSimpleStep("drop the privilege list of this role", func(ctx context.Context) ([]nestedStep, error) {
if !foreDrop {
return nil, nil
}
err := core.meta.DropGrant(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: roleName})
if err != nil {
log.Ctx(ctx).Warn("drop the privilege list failed for the role", zap.String("role_name", roleName), zap.Error(err))
}
return nil, err
}))
redoTask.AddAsyncStep(NewSimpleStep("drop role cache", func(ctx context.Context) ([]nestedStep, error) {
err := core.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheDropRole),
OpKey: roleName,
})
if err != nil {
log.Ctx(ctx).Warn("delete user role cache failed for the role", zap.String("role_name", roleName), zap.Error(err))
}
return nil, err
}))
return redoTask.Execute(ctx)
}
func executeOperateUserRoleTaskSteps(ctx context.Context, core *Core, in *milvuspb.OperateUserRoleRequest) error {
username := in.Username
roleName := in.RoleName
operateType := in.Type
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("operate user role meta data", func(ctx context.Context) ([]nestedStep, error) {
err := core.meta.OperateUserRole(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: username}, &milvuspb.RoleEntity{Name: roleName}, operateType)
if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("operate user role mata data failed",
zap.String("username", username), zap.String("role_name", roleName),
zap.Any("operate_type", operateType),
zap.Error(err))
return nil, err
}
return nil, nil
}))
redoTask.AddAsyncStep(NewSimpleStep("operate user role cache", func(ctx context.Context) ([]nestedStep, error) {
var opType int32
switch operateType {
case milvuspb.OperateUserRoleType_AddUserToRole:
opType = int32(typeutil.CacheAddUserToRole)
case milvuspb.OperateUserRoleType_RemoveUserFromRole:
opType = int32(typeutil.CacheRemoveUserFromRole)
default:
errMsg := "invalid operate type for the OperateUserRole api"
log.Ctx(ctx).Warn(errMsg,
zap.String("username", username), zap.String("role_name", roleName),
zap.Any("operate_type", operateType),
)
return nil, nil
}
if err := core.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: opType,
OpKey: funcutil.EncodeUserRoleCache(username, roleName),
}); err != nil {
log.Ctx(ctx).Warn("fail to refresh policy info cache",
zap.String("username", username), zap.String("role_name", roleName),
zap.Any("operate_type", operateType),
zap.Error(err),
)
return nil, err
}
return nil, nil
}))
return redoTask.Execute(ctx)
}
func executeOperatePrivilegeTaskSteps(ctx context.Context, core *Core, in *milvuspb.OperatePrivilegeRequest) error {
privName := in.Entity.Grantor.Privilege.Name
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("operate privilege meta data", func(ctx context.Context) ([]nestedStep, error) {
func executeOperatePrivilegeTaskSteps(ctx context.Context, core *Core, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error {
privName := entity.Grantor.Privilege.Name
if err := func() error {
// set up privilege name for metastore
dbPrivName, err := core.getMetastorePrivilegeName(ctx, privName)
if err != nil {
return nil, err
return err
}
in.Entity.Grantor.Privilege.Name = dbPrivName
entity.Grantor.Privilege.Name = dbPrivName
err = core.meta.OperatePrivilege(ctx, util.DefaultTenant, in.Entity, in.Type)
err = core.meta.OperatePrivilege(ctx, util.DefaultTenant, entity, operateType)
if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to operate the privilege", zap.Any("in", in), zap.Error(err))
return nil, err
log.Ctx(ctx).Warn("fail to operate the privilege", zap.Any("in", entity), zap.Error(err))
return err
}
return nil, nil
}))
redoTask.AddAsyncStep(NewSimpleStep("operate privilege cache", func(ctx context.Context) ([]nestedStep, error) {
return nil
}(); err != nil {
return errors.Wrap(err, "failed to operate the privilege")
}
if err := func() error {
// set back to expand privilege group
in.Entity.Grantor.Privilege.Name = privName
entity.Grantor.Privilege.Name = privName
var opType int32
switch in.Type {
switch operateType {
case milvuspb.OperatePrivilegeType_Grant:
opType = int32(typeutil.CacheGrantPrivilege)
case milvuspb.OperatePrivilegeType_Revoke:
opType = int32(typeutil.CacheRevokePrivilege)
default:
log.Ctx(ctx).Warn("invalid operate type for the OperatePrivilege api", zap.Any("in", in))
return nil, nil
log.Ctx(ctx).Warn("invalid operate type for the OperatePrivilege api", zap.Any("operate_type", operateType))
return errors.New("invalid operate type for the OperatePrivilege api")
}
grants := []*milvuspb.GrantEntity{in.Entity}
grants := []*milvuspb.GrantEntity{entity}
allGroups, err := core.getDefaultAndCustomPrivilegeGroups(ctx)
if err != nil {
return nil, err
return err
}
groups := lo.SliceToMap(allGroups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges
})
expandGrants, err := core.expandPrivilegeGroups(ctx, grants, groups)
if err != nil {
return nil, err
return err
}
// if there is same grant in the other privilege groups, the grant should not be removed from the cache
if in.Type == milvuspb.OperatePrivilegeType_Revoke {
if operateType == milvuspb.OperatePrivilegeType_Revoke {
metaGrants, err := core.meta.SelectGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: in.Entity.Role,
DbName: in.Entity.DbName,
Role: entity.Role,
DbName: entity.DbName,
})
if err != nil {
return nil, err
return err
}
metaExpandGrants, err := core.expandPrivilegeGroups(ctx, metaGrants, groups)
if err != nil {
return nil, err
return err
}
expandGrants = lo.Filter(expandGrants, func(g1 *milvuspb.GrantEntity, _ int) bool {
return !lo.ContainsBy(metaExpandGrants, func(g2 *milvuspb.GrantEntity) bool {
@ -212,45 +105,23 @@ func executeOperatePrivilegeTaskSteps(ctx context.Context, core *Core, in *milvu
OpType: opType,
OpKey: funcutil.PolicyForPrivileges(expandGrants),
}); err != nil {
log.Ctx(ctx).Warn("fail to refresh policy info cache", zap.Any("in", in), zap.Error(err))
return nil, err
log.Ctx(ctx).Warn("fail to refresh policy info cache", zap.Any("in", entity), zap.Error(err))
return err
}
}
return nil, nil
}))
return redoTask.Execute(ctx)
return nil
}(); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
return nil
}
func executeRestoreRBACTaskSteps(ctx context.Context, core *Core, in *milvuspb.RestoreRBACMetaRequest) error {
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("restore rbac meta data", func(ctx context.Context) ([]nestedStep, error) {
if err := core.meta.RestoreRBAC(ctx, util.DefaultTenant, in.RBACMeta); err != nil {
log.Ctx(ctx).Warn("fail to restore rbac meta data", zap.Any("in", in), zap.Error(err))
return nil, err
}
return nil, nil
}))
redoTask.AddAsyncStep(NewSimpleStep("operate privilege cache", func(ctx context.Context) ([]nestedStep, error) {
if err := core.proxyClientManager.RefreshPolicyInfoCache(ctx, &proxypb.RefreshPolicyInfoCacheRequest{
OpType: int32(typeutil.CacheRefresh),
}); err != nil {
log.Ctx(ctx).Warn("fail to refresh policy info cache", zap.Any("in", in), zap.Error(err))
return nil, err
}
return nil, nil
}))
return redoTask.Execute(ctx)
}
func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *milvuspb.OperatePrivilegeGroupRequest) error {
redoTask := newBaseRedoTask(core.stepExecutor)
redoTask.AddSyncStep(NewSimpleStep("operate privilege group", func(ctx context.Context) ([]nestedStep, error) {
func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *milvuspb.PrivilegeGroupInfo, operateType milvuspb.OperatePrivilegeGroupType) error {
if err := func() error {
groups, err := core.meta.ListPrivilegeGroups(ctx)
if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to list privilege groups", zap.Error(err))
return nil, err
return err
}
currGroups := lo.SliceToMap(groups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges
@ -259,34 +130,25 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *
// get roles granted to the group
roles, err := core.meta.GetPrivilegeGroupRoles(ctx, in.GroupName)
if err != nil {
return nil, err
return err
}
newGroups := make(map[string][]*milvuspb.PrivilegeEntity)
for k, v := range currGroups {
if k != in.GroupName {
newGroups[k] = v
continue
}
switch in.Type {
switch operateType {
case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup:
newPrivs := lo.Union(v, in.Privileges)
newGroups[k] = lo.UniqBy(newPrivs, func(p *milvuspb.PrivilegeEntity) string {
return p.Name
})
// check if privileges are the same privilege level
privilegeLevels := lo.SliceToMap(newPrivs, func(p *milvuspb.PrivilegeEntity) (string, struct{}) {
return util.GetPrivilegeLevel(p.Name), struct{}{}
})
if len(privilegeLevels) > 1 {
return nil, errors.New("privileges are not the same privilege level")
}
case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup:
newPrivs, _ := lo.Difference(v, in.Privileges)
newGroups[k] = newPrivs
default:
return nil, errors.New("invalid operate type")
return errors.New("invalid operate type")
}
}
@ -306,15 +168,15 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *
DbName: util.AnyWord,
})
if err != nil {
return nil, err
return err
}
currGrants, err := core.expandPrivilegeGroups(ctx, grants, currGroups)
if err != nil {
return nil, err
return err
}
newGrants, err := core.expandPrivilegeGroups(ctx, grants, newGroups)
if err != nil {
return nil, err
return err
}
toRevoke := lo.Filter(currGrants, func(item *milvuspb.GrantEntity, _ int) bool {
@ -340,7 +202,7 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *
OpKey: funcutil.PolicyForPrivileges(rolesToRevoke),
}); err != nil {
log.Ctx(ctx).Warn("fail to refresh policy info cache for revoke privileges in operate privilege group", zap.Any("in", in), zap.Error(err))
return nil, err
return err
}
}
@ -351,19 +213,16 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *
OpKey: funcutil.PolicyForPrivileges(rolesToGrant),
}); err != nil {
log.Ctx(ctx).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 err
}
}
return nil, nil
}))
redoTask.AddSyncStep(NewSimpleStep("operate privilege group meta data", func(ctx context.Context) ([]nestedStep, error) {
err := core.meta.OperatePrivilegeGroup(ctx, in.GroupName, in.Privileges, in.Type)
if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to operate privilege group", zap.Error(err))
}
return nil, err
}))
return redoTask.Execute(ctx)
return nil
}(); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
}
if err := core.meta.OperatePrivilegeGroup(ctx, in.GroupName, in.Privileges, operateType); err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to operate privilege group", zap.Error(err))
return errors.Wrap(err, "failed to operate privilege group")
}
return nil
}

View File

@ -62,7 +62,6 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/commonpbutil"
"github.com/milvus-io/milvus/pkg/v2/util/contextutil"
"github.com/milvus-io/milvus/pkg/v2/util/crypto"
"github.com/milvus-io/milvus/pkg/v2/util/expr"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
@ -488,6 +487,7 @@ func (c *Core) Init() error {
c.initOnce.Do(func() {
initError = c.initInternal()
RegisterDDLCallbacks(c)
})
log.Info("RootCoord init successfully")
@ -495,22 +495,7 @@ func (c *Core) Init() error {
}
func (c *Core) initCredentials(initCtx context.Context) error {
credInfo, _ := c.meta.GetCredential(initCtx, util.UserRoot)
if credInfo == nil {
encryptedRootPassword, err := crypto.PasswordEncrypt(Params.CommonCfg.DefaultRootPassword.GetValue())
if err != nil {
log.Ctx(initCtx).Warn("RootCoord init user root failed", zap.Error(err))
return err
}
log.Ctx(initCtx).Info("RootCoord init user root")
err = c.meta.AddCredential(initCtx, &internalpb.CredentialInfo{Username: util.UserRoot, EncryptedPassword: encryptedRootPassword})
if err != nil {
log.Ctx(initCtx).Warn("RootCoord init user root failed", zap.Error(err))
return err
}
return nil
}
return nil
return c.meta.InitCredential(initCtx)
}
func (c *Core) initRbac(initCtx context.Context) error {
@ -531,7 +516,7 @@ func (c *Core) initRbac(initCtx context.Context) error {
}
if Params.RoleCfg.Enabled.GetAsBool() {
return c.initBuiltinRoles()
return c.initBuiltinRoles(initCtx)
}
return nil
}
@ -580,22 +565,22 @@ func (c *Core) initPublicRolePrivilege(initCtx context.Context) error {
return nil
}
func (c *Core) initBuiltinRoles() error {
log := log.Ctx(c.ctx)
func (c *Core) initBuiltinRoles(ctx context.Context) error {
log := log.Ctx(ctx)
rolePrivilegesMap := Params.RoleCfg.Roles.GetAsRoleDetails()
for role, privilegesJSON := range rolePrivilegesMap {
err := c.meta.CreateRole(c.ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: role})
err := c.meta.CreateRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: role})
if err != nil && !common.IsIgnorableError(err) {
log.Error("create a builtin role fail", zap.String("roleName", role), zap.Error(err))
return errors.Wrapf(err, "failed to create a builtin role: %s", role)
}
for _, privilege := range privilegesJSON[util.RoleConfigPrivileges] {
privilegeName, err := c.getMetastorePrivilegeName(c.ctx, privilege[util.RoleConfigPrivilege])
privilegeName, err := c.getMetastorePrivilegeName(ctx, privilege[util.RoleConfigPrivilege])
if err != nil {
return errors.Wrapf(err, "failed to get metastore privilege name for: %s", privilege[util.RoleConfigPrivilege])
}
err = c.meta.OperatePrivilege(c.ctx, util.DefaultTenant, &milvuspb.GrantEntity{
err = c.meta.OperatePrivilege(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: role},
Object: &milvuspb.ObjectEntity{Name: privilege[util.RoleConfigObjectType]},
ObjectName: privilege[util.RoleConfigObjectName],
@ -2193,20 +2178,14 @@ func (c *Core) CreateCredential(ctx context.Context, credInfo *internalpb.Creden
return merr.Status(err), nil
}
// insert to db
err := c.meta.AddCredential(ctx, credInfo)
if err != nil {
ctxLog.Warn("CreateCredential save credential failed", zap.Error(err))
if err := c.broadcastAlterUserForCreateCredential(ctx, credInfo); err != nil {
ctxLog.Warn("CreateCredential failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
if errors.Is(err, errUserAlreadyExists) {
return merr.StatusWithErrorCode(errors.Errorf("user already exists: %s", credInfo.Username), commonpb.ErrorCode_CreateCredentialFailure), nil
}
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreateCredentialFailure), nil
}
// update proxy's local cache
err = c.UpdateCredCache(ctx, credInfo)
if err != nil {
ctxLog.Warn("CreateCredential add cache failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
}
ctxLog.Debug("CreateCredential success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
@ -2254,21 +2233,12 @@ func (c *Core) UpdateCredential(ctx context.Context, credInfo *internalpb.Creden
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil
}
// update data on storage
err := c.meta.AlterCredential(ctx, credInfo)
if err != nil {
ctxLog.Warn("UpdateCredential save credential failed", zap.Error(err))
if err := c.broadcastAlterUserForUpdateCredential(ctx, credInfo); err != nil {
ctxLog.Warn("UpdateCredential append message failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_UpdateCredentialFailure), nil
}
// update proxy's local cache
err = c.UpdateCredCache(ctx, credInfo)
if err != nil {
ctxLog.Warn("UpdateCredential update cache failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_UpdateCredentialFailure), nil
}
ctxLog.Debug("UpdateCredential success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
@ -2285,27 +2255,25 @@ func (c *Core) DeleteCredential(ctx context.Context, in *milvuspb.DeleteCredenti
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil
}
var status *commonpb.Status
defer func() {
if status.Code != 0 {
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
// user not found will not report to the client to achieve idempotent
if err := c.broadcastDropUserForDeleteCredential(ctx, in); err != nil {
if errors.Is(err, errUserNotFound) {
ctxLog.Info("DeleteCredential user not found, ignored")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return merr.Success(), nil
}
}()
err := executeDeleteCredentialTaskSteps(ctx, c, in.Username)
if err != nil {
errMsg := "fail to execute task when deleting the user"
ctxLog.Warn(errMsg, zap.Error(err))
status = merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_DeleteCredentialFailure)
return status, nil
ctxLog.Warn("DeleteCredential append message failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DeleteCredentialFailure), nil
}
ctxLog.Debug("DeleteCredential success")
ctxLog.Debug("DeleteCredential success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfCredentials.Dec()
status = merr.Success()
return status, nil
return merr.Success(), nil
}
// ListCredUsers list all usernames
@ -2352,12 +2320,13 @@ func (c *Core) CreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest) (
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil
}
entity := in.Entity
err := c.meta.CreateRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: entity.Name})
if err != nil {
errMsg := "fail to create role"
ctxLog.Warn(errMsg, zap.Error(err))
if err := c.broadcastCreateRole(ctx, in); err != nil {
ctxLog.Warn("fail to create role", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
if errors.Is(err, errRoleAlreadyExists) {
return merr.StatusWithErrorCode(errors.Newf("role [%s] already exists", in.GetEntity()), commonpb.ErrorCode_CreateRoleFailure), nil
}
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreateRoleFailure), nil
}
@ -2365,7 +2334,6 @@ func (c *Core) CreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest) (
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfRoles.Inc()
return merr.Success(), nil
}
@ -2386,32 +2354,13 @@ func (c *Core) DropRole(ctx context.Context, in *milvuspb.DropRoleRequest) (*com
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil
}
for util.IsBuiltinRole(in.GetRoleName()) {
err := merr.WrapErrPrivilegeNotPermitted("the role[%s] is a builtin role, which can't be dropped", in.GetRoleName())
return merr.Status(err), nil
}
if _, err := c.meta.SelectRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: in.RoleName}, false); err != nil {
errMsg := "not found the role, maybe the role isn't existed or internal system error"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_DropRoleFailure), nil
}
if !in.ForceDrop {
grantEntities, err := c.meta.SelectGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: in.RoleName},
DbName: "*",
})
if len(grantEntities) != 0 {
errMsg := "fail to drop the role that it has privileges. Use REVOKE API to revoke privileges"
ctxLog.Warn(errMsg, zap.Any("grants", grantEntities), zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_DropRoleFailure), nil
if err := c.broadcastDropRole(ctx, in); err != nil {
ctxLog.Warn("fail to drop role", zap.Error(err))
if errors.Is(err, errRoleNotExists) {
return merr.StatusWithErrorCode(errors.New("not found the role, maybe the role isn't existed or internal system error"), commonpb.ErrorCode_DropRoleFailure), nil
}
}
err := executeDropRoleTaskSteps(ctx, c, in.RoleName, in.ForceDrop)
if err != nil {
errMsg := "fail to execute task when dropping the role"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_DropRoleFailure), nil
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DropRoleFailure), nil
}
ctxLog.Debug(method+" success", zap.String("role_name", in.RoleName))
@ -2438,27 +2387,15 @@ func (c *Core) OperateUserRole(ctx context.Context, in *milvuspb.OperateUserRole
return merr.Status(err), nil
}
if _, err := c.meta.SelectRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: in.RoleName}, false); err != nil {
errMsg := "not found the role, maybe the role isn't existed or internal system error"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperateUserRoleFailure), nil
}
if in.Type != milvuspb.OperateUserRoleType_RemoveUserFromRole {
if _, err := c.meta.SelectUser(ctx, util.DefaultTenant, &milvuspb.UserEntity{Name: in.Username}, false); err != nil {
errMsg := "not found the user, maybe the user isn't existed or internal system error"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperateUserRoleFailure), nil
if err := c.broadcastOperateUserRole(ctx, in); err != nil {
ctxLog.Warn("fail to operate the user and role", zap.Error(err))
if errors.Is(err, errRoleNotExists) {
return merr.StatusWithErrorCode(errors.New("not found the role, maybe the role isn't existed or internal system error"), commonpb.ErrorCode_OperateUserRoleFailure), nil
}
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperateUserRoleFailure), nil
}
err := executeOperateUserRoleTaskSteps(ctx, c, in)
if err != nil {
errMsg := "fail to execute task when operate the user and role"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperateUserRoleFailure), nil
}
ctxLog.Debug(method + " success")
ctxLog.Info(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return merr.Success(), nil
@ -2558,14 +2495,14 @@ func (c *Core) SelectUser(ctx context.Context, in *milvuspb.SelectUserRequest) (
}, nil
}
func (c *Core) isValidRole(entity *milvuspb.RoleEntity) error {
func (c *Core) isValidRole(ctx context.Context, entity *milvuspb.RoleEntity) error {
if entity == nil {
return errors.New("the role entity is nil")
}
if entity.Name == "" {
return errors.New("the name in the role entity is empty")
}
if _, err := c.meta.SelectRole(c.ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: entity.Name}, false); err != nil {
if _, err := c.meta.SelectRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: entity.Name}, false); err != nil {
log.Warn("fail to select the role", zap.String("role_name", entity.Name), zap.Error(err))
return errors.New("not found the role, maybe the role isn't existed or internal system error")
}
@ -2642,36 +2579,11 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile
ctxLog := log.Ctx(ctx).With(zap.String("role", typeutil.RootCoordRole), zap.Any("in", in))
ctxLog.Debug(method)
if err := c.operatePrivilegeCommonCheck(ctx, in); err != nil {
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil
}
privName := in.Entity.Grantor.Privilege.Name
switch in.Version {
case "v2":
if err := c.isValidPrivilegeV2(ctx, privName); err != nil {
ctxLog.Error("", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
}
if err := c.validatePrivilegeGroupParams(ctx, privName, in.Entity.DbName, in.Entity.ObjectName); err != nil {
ctxLog.Error("", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
}
// set up object type for metastore, to be compatible with v1 version
in.Entity.Object.Name = util.GetObjectType(privName)
default:
if err := c.isValidPrivilege(ctx, privName, in.Entity.Object.Name); err != nil {
ctxLog.Error("", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
}
// set up object name if it is global object type and not built in privilege group
if in.Entity.Object.Name == commonpb.ObjectType_Global.String() && !util.IsBuiltinPrivilegeGroup(in.Entity.Grantor.Privilege.Name) {
in.Entity.ObjectName = util.AnyWord
}
}
err := executeOperatePrivilegeTaskSteps(ctx, c, in)
if err != nil {
if err := c.broadcastOperatePrivilege(ctx, in); err != nil {
errMsg := "fail to execute task when operating the privilege"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
@ -2684,9 +2596,6 @@ func (c *Core) OperatePrivilege(ctx context.Context, in *milvuspb.OperatePrivile
}
func (c *Core) operatePrivilegeCommonCheck(ctx context.Context, in *milvuspb.OperatePrivilegeRequest) error {
if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return err
}
if in.Type != milvuspb.OperatePrivilegeType_Grant && in.Type != milvuspb.OperatePrivilegeType_Revoke {
errMsg := fmt.Sprintf("invalid operate privilege type, current type: %s, valid value: [%s, %s]", in.Type, milvuspb.OperatePrivilegeType_Grant, milvuspb.OperatePrivilegeType_Revoke)
return errors.New(errMsg)
@ -2698,7 +2607,7 @@ func (c *Core) operatePrivilegeCommonCheck(ctx context.Context, in *milvuspb.Ope
if err := c.isValidObject(in.Entity.Object); err != nil {
return errors.New("the object entity in the request is nil or invalid")
}
if err := c.isValidRole(in.Entity.Role); err != nil {
if err := c.isValidRole(ctx, in.Entity.Role); err != nil {
return err
}
entity := in.Entity.Grantor
@ -2798,7 +2707,7 @@ func (c *Core) SelectGrant(ctx context.Context, in *milvuspb.SelectGrantRequest)
Status: merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_SelectGrantFailure),
}, nil
}
if err := c.isValidRole(in.Entity.Role); err != nil {
if err := c.isValidRole(ctx, in.Entity.Role); err != nil {
ctxLog.Warn("", zap.Error(err))
return &milvuspb.SelectGrantResponse{
Status: merr.StatusWithErrorCode(err, commonpb.ErrorCode_SelectGrantFailure),
@ -2939,8 +2848,11 @@ func (c *Core) RestoreRBAC(ctx context.Context, in *milvuspb.RestoreRBACMetaRequ
return merr.Status(err), nil
}
err := executeRestoreRBACTaskSteps(ctx, c, in)
if err != nil {
if err := c.broadcastRestoreRBACV2(ctx, in); err != nil {
if errors.Is(err, errEmptyRBACMeta) {
ctxLog.Info("restoring rbac meta is empty, skip restore", zap.Error(err))
return merr.Success(), nil
}
errMsg := "fail to execute task when restore rbac meta data"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil
@ -3092,12 +3004,12 @@ func (c *Core) CreatePrivilegeGroup(ctx context.Context, in *milvuspb.CreatePriv
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreatePrivilegeGroupFailure), nil
}
if err := c.meta.CreatePrivilegeGroup(ctx, in.GroupName); err != nil {
if err := c.broadcastCreatePrivilegeGroup(ctx, in); err != nil {
ctxLog.Warn("fail to create privilege group", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreatePrivilegeGroupFailure), nil
}
ctxLog.Debug(method + " success")
ctxLog.Info(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Inc()
@ -3115,12 +3027,16 @@ func (c *Core) DropPrivilegeGroup(ctx context.Context, in *milvuspb.DropPrivileg
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DropPrivilegeGroupFailure), nil
}
if err := c.meta.DropPrivilegeGroup(ctx, in.GroupName); err != nil {
if err := c.broadcastDropPrivilegeGroup(ctx, in); err != nil {
if errors.Is(err, errNotCustomPrivilegeGroup) {
ctxLog.Info("privilege group is not custom privilege group, skip drop", zap.Error(err))
return merr.Success(), nil
}
ctxLog.Warn("fail to drop privilege group", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DropPrivilegeGroupFailure), nil
}
ctxLog.Debug(method + " success")
ctxLog.Info(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Desc()
@ -3171,8 +3087,7 @@ func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePr
return merr.Status(err), nil
}
err := executeOperatePrivilegeGroupTaskSteps(ctx, c, in)
if err != nil {
if err := c.broadcastOperatePrivilegeGroup(ctx, in); err != nil {
errMsg := "fail to execute task when operate privilege group"
ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeGroupFailure), nil

View File

@ -30,15 +30,22 @@ import (
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/distributed/streaming"
"github.com/milvus-io/milvus/internal/metastore/model"
"github.com/milvus-io/milvus/internal/mocks/distributed/mock_streaming"
"github.com/milvus-io/milvus/internal/mocks/streamingcoord/server/mock_broadcaster"
mockrootcoord "github.com/milvus-io/milvus/internal/rootcoord/mocks"
"github.com/milvus-io/milvus/internal/util/proxyutil"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/broadcast"
"github.com/milvus-io/milvus/internal/streamingcoord/server/broadcaster/registry"
"github.com/milvus-io/milvus/internal/util/sessionutil"
"github.com/milvus-io/milvus/pkg/v2/common"
"github.com/milvus-io/milvus/pkg/v2/proto/etcdpb"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb"
"github.com/milvus-io/milvus/pkg/v2/proto/rootcoordpb"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/message"
"github.com/milvus-io/milvus/pkg/v2/streaming/util/types"
"github.com/milvus-io/milvus/pkg/v2/streaming/walimpls/impls/walimplstest"
"github.com/milvus-io/milvus/pkg/v2/util"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr"
@ -51,15 +58,39 @@ import (
func TestMain(m *testing.M) {
paramtable.Init()
rand.Seed(time.Now().UnixNano())
parameters := []string{"tikv", "etcd"}
var code int
for _, v := range parameters {
paramtable.Get().Save(paramtable.Get().MetaStoreCfg.MetaStoreType.Key, v)
code = m.Run()
}
code := m.Run()
os.Exit(code)
}
func initStreamingSystem() {
t := common.NewEmptyMockT()
wal := mock_streaming.NewMockWALAccesser(t)
wal.EXPECT().ControlChannel().Return(funcutil.GetControlChannel("by-dev-rootcoord-dml_0"))
streaming.SetWALForTest(wal)
bapi := mock_broadcaster.NewMockBroadcastAPI(t)
bapi.EXPECT().Broadcast(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, msg message.BroadcastMutableMessage) (*types.BroadcastAppendResult, error) {
results := make(map[string]*message.AppendResult)
for _, vchannel := range msg.BroadcastHeader().VChannels {
results[vchannel] = &message.AppendResult{
MessageID: walimplstest.NewTestMessageID(1),
TimeTick: tsoutil.ComposeTSByTime(time.Now(), 0),
LastConfirmedMessageID: walimplstest.NewTestMessageID(1),
}
}
registry.CallMessageAckCallback(context.Background(), msg, results)
return &types.BroadcastAppendResult{}, nil
})
bapi.EXPECT().Close().Return()
mb := mock_broadcaster.NewMockBroadcaster(t)
mb.EXPECT().WithResourceKeys(mock.Anything, mock.Anything).Return(bapi, nil)
mb.EXPECT().Close().Return()
broadcast.Release()
broadcast.ResetBroadcaster()
broadcast.Register(mb)
}
func TestRootCoord_CreateDatabase(t *testing.T) {
t.Run("not healthy", func(t *testing.T) {
c := newTestCore(withAbnormalCode())
@ -1733,345 +1764,6 @@ func TestRootCoord_DescribeDatabase(t *testing.T) {
})
}
func TestRootCoord_RBACError(t *testing.T) {
ctx := context.Background()
c := newTestCore(withHealthyCode(), withInvalidMeta())
t.Run("create credential failed", func(t *testing.T) {
resp, err := c.CreateCredential(ctx, &internalpb.CredentialInfo{Username: "foo", EncryptedPassword: "bar"})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
})
t.Run("get credential failed", func(t *testing.T) {
resp, err := c.GetCredential(ctx, &rootcoordpb.GetCredentialRequest{Username: "foo"})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
})
t.Run("update credential failed", func(t *testing.T) {
resp, err := c.UpdateCredential(ctx, &internalpb.CredentialInfo{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
})
t.Run("delete credential failed", func(t *testing.T) {
resp, err := c.DeleteCredential(ctx, &milvuspb.DeleteCredentialRequest{Username: "foo"})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
})
t.Run("list credential failed", func(t *testing.T) {
resp, err := c.ListCredUsers(ctx, &milvuspb.ListCredUsersRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
})
t.Run("create role failed", func(t *testing.T) {
resp, err := c.CreateRole(ctx, &milvuspb.CreateRoleRequest{Entity: &milvuspb.RoleEntity{Name: "foo"}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
})
t.Run("drop role failed", func(t *testing.T) {
resp, err := c.DropRole(ctx, &milvuspb.DropRoleRequest{RoleName: "foo"})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
})
t.Run("operate user role failed", func(t *testing.T) {
mockMeta := c.meta.(*mockMetaTable)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, nil
}
mockMeta.SelectUserFunc = func(ctx context.Context, tenant string, entity *milvuspb.UserEntity, includeRoleInfo bool) ([]*milvuspb.UserResult, error) {
return nil, nil
}
resp, err := c.OperateUserRole(ctx, &milvuspb.OperateUserRoleRequest{RoleName: "foo", Username: "bar", Type: milvuspb.OperateUserRoleType_AddUserToRole})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, errors.New("mock error")
}
mockMeta.SelectUserFunc = func(ctx context.Context, tenant string, entity *milvuspb.UserEntity, includeRoleInfo bool) ([]*milvuspb.UserResult, error) {
return nil, errors.New("mock error")
}
})
t.Run("select role failed", func(t *testing.T) {
{
resp, err := c.SelectRole(ctx, &milvuspb.SelectRoleRequest{Role: &milvuspb.RoleEntity{Name: "foo"}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
{
resp, err := c.SelectRole(ctx, &milvuspb.SelectRoleRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
})
t.Run("select user failed", func(t *testing.T) {
{
resp, err := c.SelectUser(ctx, &milvuspb.SelectUserRequest{User: &milvuspb.UserEntity{Name: "foo"}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
{
resp, err := c.SelectUser(ctx, &milvuspb.SelectUserRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
})
t.Run("operate privilege failed", func(t *testing.T) {
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Type: milvuspb.OperatePrivilegeType(100)})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
}
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Type: milvuspb.OperatePrivilegeType_Grant})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
}
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Entity: &milvuspb.GrantEntity{Object: &milvuspb.ObjectEntity{Name: "CollectionErr"}}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
}
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Entity: &milvuspb.GrantEntity{Object: &milvuspb.ObjectEntity{Name: "Collection"}, Role: &milvuspb.RoleEntity{Name: "foo"}}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
}
mockMeta := c.meta.(*mockMetaTable)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, nil
}
mockMeta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, nil
}
{
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: "foo"},
Object: &milvuspb.ObjectEntity{Name: "Collection"},
ObjectName: "col1",
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "root"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Insert"},
},
}, Type: milvuspb.OperatePrivilegeType_Grant})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
}
mockMeta.IsCustomPrivilegeGroupFunc = func(ctx context.Context, groupName string) (bool, error) {
return false, nil
}
mockMeta.SelectUserFunc = func(ctx context.Context, tenant string, entity *milvuspb.UserEntity, includeRoleInfo bool) ([]*milvuspb.UserResult, error) {
return nil, nil
}
resp, err := c.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: "foo"},
Object: &milvuspb.ObjectEntity{Name: "Collection"},
ObjectName: "col1",
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: "root"},
Privilege: &milvuspb.PrivilegeEntity{Name: "Insert"},
},
}, Type: milvuspb.OperatePrivilegeType_Grant})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.ErrorCode)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, errors.New("mock error")
}
mockMeta.SelectUserFunc = func(ctx context.Context, tenant string, entity *milvuspb.UserEntity, includeRoleInfo bool) ([]*milvuspb.UserResult, error) {
return nil, errors.New("mock error")
}
})
t.Run("operate privilege group failed", func(t *testing.T) {
mockMeta := c.meta.(*mockMetaTable)
mockMeta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, errors.New("mock error")
}
mockMeta.CreatePrivilegeGroupFunc = func(ctx context.Context, groupName string) error {
return errors.New("mock error")
}
mockMeta.GetPrivilegeGroupRolesFunc = func(ctx context.Context, groupName string) ([]*milvuspb.RoleEntity, error) {
return nil, errors.New("mock error")
}
{
resp, err := c.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
}
{
resp, err := c.ListPrivilegeGroups(ctx, &milvuspb.ListPrivilegeGroupsRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
{
resp, err := c.OperatePrivilegeGroup(ctx, &milvuspb.OperatePrivilegeGroupRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
}
{
resp, err := c.CreatePrivilegeGroup(ctx, &milvuspb.CreatePrivilegeGroupRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetErrorCode())
}
})
t.Run("select grant failed", func(t *testing.T) {
{
resp, err := c.SelectGrant(ctx, &milvuspb.SelectGrantRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
{
resp, err := c.SelectGrant(ctx, &milvuspb.SelectGrantRequest{Entity: &milvuspb.GrantEntity{Role: &milvuspb.RoleEntity{Name: "foo"}}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
mockMeta := c.meta.(*mockMetaTable)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, nil
}
{
resp, err := c.SelectGrant(ctx, &milvuspb.SelectGrantRequest{Entity: &milvuspb.GrantEntity{Role: &milvuspb.RoleEntity{Name: "foo"}, Object: &milvuspb.ObjectEntity{Name: "CollectionFoo"}}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
{
resp, err := c.SelectGrant(ctx, &milvuspb.SelectGrantRequest{Entity: &milvuspb.GrantEntity{Role: &milvuspb.RoleEntity{Name: "foo"}, Object: &milvuspb.ObjectEntity{Name: "Collection"}}})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, errors.New("mock error")
}
})
t.Run("select grant success", func(t *testing.T) {
mockMeta := c.meta.(*mockMetaTable)
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return []*milvuspb.RoleResult{
{
Role: &milvuspb.RoleEntity{Name: "foo"},
},
}, nil
}
mockMeta.SelectGrantFunc = func(ctx context.Context, tenant string, entity *milvuspb.GrantEntity) ([]*milvuspb.GrantEntity, error) {
return []*milvuspb.GrantEntity{
{
Role: &milvuspb.RoleEntity{Name: "foo"},
},
}, merr.ErrIoKeyNotFound
}
{
resp, err := c.SelectGrant(ctx, &milvuspb.SelectGrantRequest{Entity: &milvuspb.GrantEntity{Role: &milvuspb.RoleEntity{Name: "foo"}, Object: &milvuspb.ObjectEntity{Name: "Collection"}, ObjectName: "fir"}})
assert.NoError(t, err)
assert.Equal(t, 1, len(resp.GetEntities()))
assert.Equal(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
}
mockMeta.SelectRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity, includeUserInfo bool) ([]*milvuspb.RoleResult, error) {
return nil, errors.New("mock error")
}
mockMeta.SelectGrantFunc = func(ctx context.Context, tenant string, entity *milvuspb.GrantEntity) ([]*milvuspb.GrantEntity, error) {
return nil, errors.New("mock error")
}
})
t.Run("list policy failed", func(t *testing.T) {
resp, err := c.ListPolicy(ctx, &internalpb.ListPolicyRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
mockMeta := c.meta.(*mockMetaTable)
mockMeta.ListPolicyFunc = func(ctx context.Context, tenant string) ([]*milvuspb.GrantEntity, error) {
return []*milvuspb.GrantEntity{{
ObjectName: "*",
Object: &milvuspb.ObjectEntity{
Name: "Global",
},
Role: &milvuspb.RoleEntity{Name: "role"},
Grantor: &milvuspb.GrantorEntity{Privilege: &milvuspb.PrivilegeEntity{Name: "CustomGroup"}},
}}, nil
}
resp, err = c.ListPolicy(ctx, &internalpb.ListPolicyRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
mockMeta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
return []*milvuspb.PrivilegeGroupInfo{
{
GroupName: "CollectionAdmin",
Privileges: []*milvuspb.PrivilegeEntity{{Name: "CreateCollection"}},
},
}, nil
}
resp, err = c.ListPolicy(ctx, &internalpb.ListPolicyRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
mockMeta.IsCustomPrivilegeGroupFunc = func(ctx context.Context, groupName string) (bool, error) {
return true, nil
}
resp, err = c.ListPolicy(ctx, &internalpb.ListPolicyRequest{})
assert.NoError(t, err)
assert.NotEqual(t, commonpb.ErrorCode_Success, resp.GetStatus().GetErrorCode())
mockMeta.ListPolicyFunc = func(ctx context.Context, tenant string) ([]*milvuspb.GrantEntity, error) {
return []*milvuspb.GrantEntity{}, errors.New("mock error")
}
mockMeta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
return []*milvuspb.PrivilegeGroupInfo{}, errors.New("mock error")
}
mockMeta.IsCustomPrivilegeGroupFunc = func(ctx context.Context, groupName string) (bool, error) {
return false, errors.New("mock error")
}
})
}
func TestRootCoord_BuiltinRoles(t *testing.T) {
roleDbAdmin := "db_admin"
paramtable.Init()
paramtable.Get().Save(paramtable.Get().RoleCfg.Enabled.Key, "true")
paramtable.Get().Save(paramtable.Get().RoleCfg.Roles.Key, `{"`+roleDbAdmin+`": {"privileges": [{"object_type": "Global", "object_name": "*", "privilege": "CreateCollection", "db_name": "*"}]}}`)
t.Run("init builtin roles success", func(t *testing.T) {
c := newTestCore(withHealthyCode(), withInvalidMeta())
mockMeta := c.meta.(*mockMetaTable)
mockMeta.CreateRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
return nil
}
mockMeta.OperatePrivilegeFunc = func(ctx context.Context, tenant string, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error {
return nil
}
mockMeta.ListPrivilegeGroupsFunc = func(ctx context.Context) ([]*milvuspb.PrivilegeGroupInfo, error) {
return nil, nil
}
err := c.initBuiltinRoles()
assert.Equal(t, nil, err)
assert.True(t, util.IsBuiltinRole(roleDbAdmin))
assert.False(t, util.IsBuiltinRole(util.RoleAdmin))
resp, err := c.DropRole(context.Background(), &milvuspb.DropRoleRequest{RoleName: roleDbAdmin})
assert.Equal(t, nil, err)
assert.Equal(t, int32(1401), resp.Code) // merr.ErrPrivilegeNotPermitted
})
t.Run("init builtin roles fail to create role", func(t *testing.T) {
c := newTestCore(withHealthyCode(), withInvalidMeta())
mockMeta := c.meta.(*mockMetaTable)
mockMeta.CreateRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
return merr.ErrPrivilegeNotPermitted
}
err := c.initBuiltinRoles()
assert.Error(t, err)
})
t.Run("init builtin roles fail to operate privileg", func(t *testing.T) {
c := newTestCore(withHealthyCode(), withInvalidMeta())
mockMeta := c.meta.(*mockMetaTable)
mockMeta.CreateRoleFunc = func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
return nil
}
mockMeta.OperatePrivilegeFunc = func(ctx context.Context, tenant string, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error {
return merr.ErrPrivilegeNotPermitted
}
err := c.initBuiltinRoles()
assert.Error(t, err)
})
}
func TestCore_Stop(t *testing.T) {
t.Run("abnormal stop before component is ready", func(t *testing.T) {
c := &Core{}
@ -2171,25 +1863,6 @@ func TestCore_BackupRBAC(t *testing.T) {
assert.False(t, merr.Ok(resp.GetStatus()))
}
func TestCore_RestoreRBAC(t *testing.T) {
meta := mockrootcoord.NewIMetaTable(t)
c := newTestCore(withHealthyCode(), withMeta(meta))
mockProxyClientManager := proxyutil.NewMockProxyClientManager(t)
mockProxyClientManager.EXPECT().RefreshPolicyInfoCache(mock.Anything, mock.Anything).Return(nil).Maybe()
c.proxyClientManager = mockProxyClientManager
meta.EXPECT().RestoreRBAC(mock.Anything, mock.Anything, mock.Anything).Return(nil)
resp, err := c.RestoreRBAC(context.Background(), &milvuspb.RestoreRBACMetaRequest{})
assert.NoError(t, err)
assert.True(t, merr.Ok(resp))
meta.ExpectedCalls = nil
meta.EXPECT().RestoreRBAC(mock.Anything, mock.Anything, mock.Anything).Return(errors.New("mock error"))
resp, err = c.RestoreRBAC(context.Background(), &milvuspb.RestoreRBACMetaRequest{})
assert.NoError(t, err)
assert.False(t, merr.Ok(resp))
}
func TestCore_getMetastorePrivilegeName(t *testing.T) {
meta := mockrootcoord.NewIMetaTable(t)
c := newTestCore(withHealthyCode(), withMeta(meta))

View File

@ -259,13 +259,13 @@ message ChannelTimeTickMsg {
}
message CredentialInfo {
string username = 1;
string username = 1; // not save in metadata.
// encrypted by bcrypt (for higher security level)
string encrypted_password = 2;
string tenant = 3;
bool is_super = 4;
string tenant = 3 [deprecated=true]; // not used.
bool is_super = 4 [deprecated=true]; // not used.
// encrypted by sha256 (for good performance in cache mapping)
string sha256_password = 5;
string sha256_password = 5; // not save in metadata.
uint64 time_tick = 6; // the timetick in wal which the credential updates
}

View File

@ -2470,14 +2470,16 @@ type CredentialInfo struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` // not save in metadata.
// encrypted by bcrypt (for higher security level)
EncryptedPassword string `protobuf:"bytes,2,opt,name=encrypted_password,json=encryptedPassword,proto3" json:"encrypted_password,omitempty"`
Tenant string `protobuf:"bytes,3,opt,name=tenant,proto3" json:"tenant,omitempty"`
IsSuper bool `protobuf:"varint,4,opt,name=is_super,json=isSuper,proto3" json:"is_super,omitempty"`
// Deprecated: Marked as deprecated in internal.proto.
Tenant string `protobuf:"bytes,3,opt,name=tenant,proto3" json:"tenant,omitempty"` // not used.
// Deprecated: Marked as deprecated in internal.proto.
IsSuper bool `protobuf:"varint,4,opt,name=is_super,json=isSuper,proto3" json:"is_super,omitempty"` // not used.
// encrypted by sha256 (for good performance in cache mapping)
Sha256Password string `protobuf:"bytes,5,opt,name=sha256_password,json=sha256Password,proto3" json:"sha256_password,omitempty"`
TimeTick uint64 `protobuf:"varint,6,opt,name=time_tick,json=timeTick,proto3" json:"time_tick,omitempty"` // the timetick in wal which the credential updates
Sha256Password string `protobuf:"bytes,5,opt,name=sha256_password,json=sha256Password,proto3" json:"sha256_password,omitempty"` // not save in metadata.
TimeTick uint64 `protobuf:"varint,6,opt,name=time_tick,json=timeTick,proto3" json:"time_tick,omitempty"` // the timetick in wal which the credential updates
}
func (x *CredentialInfo) Reset() {
@ -2526,6 +2528,7 @@ func (x *CredentialInfo) GetEncryptedPassword() string {
return ""
}
// Deprecated: Marked as deprecated in internal.proto.
func (x *CredentialInfo) GetTenant() string {
if x != nil {
return x.Tenant
@ -2533,6 +2536,7 @@ func (x *CredentialInfo) GetTenant() string {
return ""
}
// Deprecated: Marked as deprecated in internal.proto.
func (x *CredentialInfo) GetIsSuper() bool {
if x != nil {
return x.IsSuper
@ -4485,283 +4489,284 @@ var file_internal_proto_rawDesc = []byte{
0x28, 0x04, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x12, 0x2b,
0x0a, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x64, 0x65, 0x66, 0x61, 0x75,
0x6c, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xd4, 0x01, 0x0a, 0x0e,
0x6c, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0xdc, 0x01, 0x0a, 0x0e,
0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a,
0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x65, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x50, 0x61, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x69,
0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x69,
0x63, 0x6b, 0x22, 0x45, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42,
0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x12, 0x4c, 0x69,
0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f,
0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c,
0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72,
0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73,
0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x76, 0x69,
0x6c, 0x65, 0x67, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67,
0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x76,
0x69, 0x6c, 0x65, 0x67, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x22, 0x67, 0x0a, 0x19, 0x53,
0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67,
0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61,
0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74,
0x74, 0x65, 0x72, 0x6e, 0x22, 0x9a, 0x01, 0x0a, 0x1a, 0x53, 0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x47, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61,
0x69, 0x72, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x22, 0x45, 0x0a, 0x04, 0x52, 0x61, 0x74, 0x65, 0x12, 0x2f, 0x0a, 0x02, 0x72, 0x74, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x52, 0x61,
0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02, 0x72, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18,
0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x72, 0x22, 0x32, 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18,
0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x22, 0xb7, 0x03, 0x0a,
0x15, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x16, 0x0a, 0x04, 0x64, 0x62, 0x49, 0x44, 0x18, 0x01,
0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x64, 0x62, 0x49, 0x44, 0x12, 0x22,
0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x49, 0x44, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c,
0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70,
0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
0x03, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12,
0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73,
0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e,
0x61, 0x6d, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68,
0x65, 0x6d, 0x61, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07,
0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e,
0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x06, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x74,
0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65,
0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x69, 0x73, 0x53,
0x75, 0x70, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x5f, 0x70,
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73,
0x68, 0x61, 0x32, 0x35, 0x36, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a,
0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04,
0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x22, 0x45, 0x0a, 0x11, 0x4c, 0x69,
0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72,
0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74,
0x61, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28,
0x04, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52,
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0xee, 0x01, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61,
0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46,
0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69,
0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x07,
0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x5b, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76,
0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14,
0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a,
0x6f, 0x62, 0x49, 0x44, 0x22, 0x49, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62,
0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22,
0x81, 0x02, 0x0a, 0x12, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67,
0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67,
0x72, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x77, 0x73,
0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x6f,
0x77, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52,
0x6f, 0x77, 0x73, 0x22, 0xc6, 0x03, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d,
0x70, 0x6f, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74,
0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x70, 0x72,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73,
0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0e, 0x74, 0x61, 0x73, 0x6b, 0x50,
0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d,
0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x09, 0x20, 0x01,
0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a,
0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x54, 0x0a, 0x1a,
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x62,
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x64, 0x62, 0x49, 0x44, 0x12, 0x22,
0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02,
0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x49, 0x44, 0x22, 0x56, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x86, 0x02, 0x0a, 0x13, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x49, 0x44,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x73, 0x12,
0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32,
0x25, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4a, 0x6f,
0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x18,
0x0a, 0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67,
0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0a, 0x70, 0x72,
0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6c, 0x6c,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,
0x6d, 0x65, 0x73, 0x22, 0x74, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a,
0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64,
0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6c,
0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x67,
0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0a, 0x73,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x22, 0x3f, 0x0a, 0x0b, 0x46, 0x69, 0x65,
0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x69, 0x65, 0x6c,
0x64, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x03, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x49, 0x44, 0x73, 0x22, 0x82, 0x04, 0x0a, 0x0b, 0x53,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65,
0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c,
0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b,
0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a,
0x0a, 0x08, 0x76, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x76, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75,
0x6d, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75,
0x6d, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37,
0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c,
0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x73, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x53, 0x6f,
0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6c,
0x6f, 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76,
0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x0a, 0x69,
0x6e, 0x73, 0x65, 0x72, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x64, 0x65, 0x6c,
0x74, 0x61, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f,
0x67, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x41, 0x0a, 0x0a,
0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x69,
0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x73, 0x4c, 0x6f, 0x67, 0x73, 0x22,
0x96, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x49,
0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73,
0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73,
0x65, 0x22, 0xdf, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a,
0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x73,
0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03,
0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12,
0x52, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x5f, 0x67, 0x72, 0x6f,
0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x69, 0x6c, 0x76,
0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e,
0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e,
0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x47, 0x72, 0x6f,
0x75, 0x70, 0x73, 0x22, 0x67, 0x0a, 0x19, 0x53, 0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61,
0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x9a, 0x01, 0x0a,
0x1a, 0x53, 0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69,
0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x12, 0x46, 0x0a, 0x0c, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x73, 0x65, 0x67, 0x6d,
0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x51,
0x75, 0x6f, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04,
0x62, 0x61, 0x73, 0x65, 0x22, 0x71, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61,
0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f,
0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x2a, 0x45, 0x0a, 0x09, 0x52, 0x61, 0x74, 0x65, 0x53,
0x63, 0x6f, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x10,
0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x10, 0x01, 0x12,
0x0e, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x02, 0x12,
0x0d, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, 0x2a, 0xc4,
0x01, 0x0a, 0x08, 0x52, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x44,
0x44, 0x4c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10,
0x0a, 0x0c, 0x44, 0x44, 0x4c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x01,
0x12, 0x0c, 0x0a, 0x08, 0x44, 0x44, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x10, 0x02, 0x12, 0x0c,
0x0a, 0x08, 0x44, 0x44, 0x4c, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d,
0x44, 0x44, 0x4c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12,
0x0d, 0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x10, 0x05, 0x12, 0x0d,
0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, 0x06, 0x12, 0x0f, 0x0a,
0x0b, 0x44, 0x4d, 0x4c, 0x42, 0x75, 0x6c, 0x6b, 0x4c, 0x6f, 0x61, 0x64, 0x10, 0x07, 0x12, 0x0d,
0x0a, 0x09, 0x44, 0x51, 0x4c, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x10, 0x08, 0x12, 0x0c, 0x0a,
0x08, 0x44, 0x51, 0x4c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x44,
0x4d, 0x4c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x44,
0x4c, 0x44, 0x42, 0x10, 0x0b, 0x2a, 0x83, 0x01, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65,
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12,
0x10, 0x0a, 0x0c, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10,
0x02, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03,
0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09,
0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x06, 0x12, 0x0b,
0x0a, 0x07, 0x53, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x07, 0x42, 0x35, 0x5a, 0x33, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73,
0x2d, 0x69, 0x6f, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76,
0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x12, 0x47, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65,
0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x45, 0x0a, 0x04, 0x52, 0x61, 0x74,
0x65, 0x12, 0x2f, 0x0a, 0x02, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02,
0x72, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x72,
0x22, 0x32, 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14,
0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70,
0x61, 0x74, 0x68, 0x73, 0x22, 0xb7, 0x03, 0x0a, 0x15, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x16,
0x0a, 0x04, 0x64, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01,
0x52, 0x04, 0x64, 0x62, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f,
0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f,
0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x49, 0x44, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x06,
0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d,
0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x63, 0x68, 0x65,
0x6d, 0x61, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68,
0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x37, 0x0a, 0x05, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c,
0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66,
0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49,
0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0xee,
0x01, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c,
0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x6c,
0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c,
0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
0x5b, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0x49, 0x0a, 0x18,
0x47, 0x65, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0x81, 0x02, 0x0a, 0x12, 0x49, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b,
0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66,
0x69, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08,
0x66, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01,
0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d,
0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x69, 0x6d,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c,
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a,
0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03,
0x52, 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x52, 0x6f, 0x77, 0x73, 0x22, 0xc6, 0x03, 0x0a, 0x19,
0x47, 0x65, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,
0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76,
0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3b,
0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x53,
0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18,
0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12,
0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70,
0x6c, 0x65, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x52, 0x0a,
0x0f, 0x74, 0x61, 0x73, 0x6b, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49,
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x54, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73,
0x73, 0x52, 0x0e, 0x74, 0x61, 0x73, 0x6b, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65,
0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x6f,
0x77, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74,
0x65, 0x64, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
0x72, 0x6f, 0x77, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61,
0x6c, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x22, 0x54, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f,
0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
0x52, 0x04, 0x64, 0x62, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f,
0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x56, 0x0a, 0x12, 0x4c, 0x69,
0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c,
0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,
0x6d, 0x65, 0x22, 0x86, 0x02, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6d, 0x70, 0x6f, 0x72,
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c,
0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
0x16, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
0x06, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x73, 0x12, 0x3d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65,
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e,
0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06,
0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73,
0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x05,
0x20, 0x03, 0x28, 0x03, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6c, 0x6c,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x74, 0x0a, 0x16, 0x47,
0x65, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a,
0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x73, 0x18,
0x03, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0a, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44,
0x73, 0x22, 0x3f, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67,
0x12, 0x18, 0x0a, 0x07, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
0x03, 0x52, 0x07, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f,
0x67, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x03, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x49,
0x44, 0x73, 0x22, 0x82, 0x04, 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18,
0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44,
0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44,
0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x74, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x43, 0x68, 0x61, 0x6e, 0x6e,
0x65, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x75, 0x6d, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x05,
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6e, 0x75, 0x6d, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x37, 0x0a,
0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d,
0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52,
0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18,
0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x67, 0x6d,
0x65, 0x6e, 0x74, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12,
0x1b, 0x0a, 0x09, 0x69, 0x73, 0x5f, 0x73, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01,
0x28, 0x08, 0x52, 0x08, 0x69, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0b,
0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42,
0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x4c, 0x6f, 0x67,
0x73, 0x12, 0x41, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x6c, 0x6f, 0x67, 0x73, 0x18,
0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x46, 0x69,
0x65, 0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x74, 0x61,
0x4c, 0x6f, 0x67, 0x73, 0x12, 0x41, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x6c, 0x6f,
0x67, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x52, 0x09, 0x73, 0x74,
0x61, 0x74, 0x73, 0x4c, 0x6f, 0x67, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x0c, 0x73, 0x65, 0x67, 0x6d,
0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22,
0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e,
0x66, 0x6f, 0x52, 0x0c, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73,
0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61,
0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75,
0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d,
0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73, 0x65, 0x22, 0x71, 0x0a, 0x17,
0x47, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c,
0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x2a,
0x45, 0x0a, 0x09, 0x52, 0x61, 0x74, 0x65, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07,
0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x61, 0x74,
0x61, 0x62, 0x61, 0x73, 0x65, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, 0x2a, 0xc4, 0x01, 0x0a, 0x08, 0x52, 0x61, 0x74, 0x65, 0x54,
0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x44, 0x4c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x44, 0x4c, 0x50, 0x61, 0x72,
0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x44, 0x4c, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x44, 0x4c, 0x46, 0x6c, 0x75,
0x73, 0x68, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x44, 0x4c, 0x43, 0x6f, 0x6d, 0x70, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x49, 0x6e,
0x73, 0x65, 0x72, 0x74, 0x10, 0x05, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x10, 0x06, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x4d, 0x4c, 0x42, 0x75, 0x6c, 0x6b,
0x4c, 0x6f, 0x61, 0x64, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x51, 0x4c, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x10, 0x08, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x51, 0x4c, 0x51, 0x75, 0x65, 0x72,
0x79, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74,
0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x44, 0x4c, 0x44, 0x42, 0x10, 0x0b, 0x2a, 0x83, 0x01,
0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65,
0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x65,
0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x50, 0x72, 0x65, 0x49, 0x6d,
0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x6d, 0x70,
0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c,
0x65, 0x64, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
0x64, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x75, 0x69, 0x6c,
0x64, 0x69, 0x6e, 0x67, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x6f, 0x72, 0x74, 0x69, 0x6e,
0x67, 0x10, 0x07, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2d, 0x69, 0x6f, 0x2f, 0x6d, 0x69, 0x6c, 0x76,
0x75, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (