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 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) 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 does exactly the same as CreateCredential
AlterCredential(ctx context.Context, credential *model.Credential) error AlterCredential(ctx context.Context, credential *model.Credential) error
// DropCredential removes the credential of this username // 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) 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) 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 { if err != nil {
log.Ctx(ctx).Error("create credential marshal fail", zap.String("key", k), zap.Error(err)) log.Ctx(ctx).Error("create credential marshal fail", zap.String("key", k), zap.Error(err))
return 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)) log.Ctx(ctx).Error("create credential persist meta fail", zap.String("key", k), zap.Error(err))
return err return err
} }
return nil 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) { func (kc *Catalog) listPartitionsAfter210(ctx context.Context, collectionID typeutil.UniqueID, ts typeutil.Timestamp) ([]*model.Partition, error) {
prefix := BuildPartitionPrefix(collectionID) prefix := BuildPartitionPrefix(collectionID)
_, values, err := kc.Snapshot.LoadWithPrefix(ctx, prefix, ts) _, 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 { if err != nil {
return nil, fmt.Errorf("unmarshal credential info err:%w", err) return nil, fmt.Errorf("unmarshal credential info err:%w", err)
} }
// we don't save the username in the credential info, so we need to set it manually from path.
return &model.Credential{Username: username, EncryptedPassword: credentialInfo.EncryptedPassword}, nil credentialInfo.Username = username
return model.UnmarshalCredentialModel(&credentialInfo), nil
} }
func (kc *Catalog) AlterAlias(ctx context.Context, alias *model.Alias, ts typeutil.Timestamp) error { 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 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 { func (kc *Catalog) remove(ctx context.Context, k string) error {
var err error var err error
if _, err = kc.Txn.Load(ctx, k); err != nil && !errors.Is(err, merr.ErrIoKeyNotFound) { 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 { func (kc *Catalog) CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error {
k := funcutil.HandleTenantForEtcdKey(RolePrefix, tenant, entity.Name) k := funcutil.HandleTenantForEtcdKey(RolePrefix, tenant, entity.Name)
err := kc.save(ctx, k) return kc.Txn.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
} }
func (kc *Catalog) DropRole(ctx context.Context, tenant string, roleName string) error { 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 { 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)) k := funcutil.HandleTenantForEtcdKey(RoleMappingPrefix, tenant, fmt.Sprintf("%s/%s", userEntity.Name, roleEntity.Name))
var err error switch operateType {
if operateType == milvuspb.OperateUserRoleType_AddUserToRole { case milvuspb.OperateUserRoleType_AddUserToRole:
err = kc.save(ctx, k) return kc.Txn.Save(ctx, k, "")
if err != nil { case milvuspb.OperateUserRoleType_RemoveUserFromRole:
log.Ctx(ctx).Error("fail to save the user-role", zap.String("key", k), zap.Error(err)) return kc.Txn.Remove(ctx, k)
}
} 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)
} }
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) { 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 { 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() { for _, role := range meta.GetRoles() {
if _, ok := existRoleMap[role.GetName()]; ok { if err := kc.CreateRole(ctx, tenant, role); err != nil {
log.Ctx(ctx).Warn("failed to restore, role already exists", zap.String("role", role.GetName())) return errors.Wrap(err, "failed to create role")
err = errors.Newf("role [%s] already exists", role.GetName())
return err
} }
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() { for _, group := range meta.GetPrivilegeGroups() {
if _, ok := existPrivGroupMap[group.GetGroupName()]; ok { if err := kc.SavePrivilegeGroup(ctx, group); err != nil {
log.Ctx(ctx).Warn("failed to restore, privilege group already exists", zap.String("group", group.GetGroupName())) return errors.Wrap(err, "failed to save privilege group")
err = errors.Newf("privilege group [%s] already exists", group.GetGroupName())
return err
} }
err = kc.SavePrivilegeGroup(ctx, group)
if err != nil {
return err
}
needRollbackPrivilegeGroups = append(needRollbackPrivilegeGroups, group)
} }
// restore grant, list latest privilege group first
existPrivGroups, err = kc.ListPrivilegeGroups(ctx)
if err != nil {
return err
}
existPrivGroupMap = lo.SliceToMap(existPrivGroups, func(entity *milvuspb.PrivilegeGroupInfo) (string, struct{}) { return entity.GetGroupName(), struct{}{} })
for _, grant := range meta.GetGrants() { for _, grant := range meta.GetGrants() {
privName := grant.GetGrantor().GetPrivilege().GetName() privName := grant.GetGrantor().GetPrivilege().GetName()
if util.IsPrivilegeNameDefined(privName) { if util.IsPrivilegeNameDefined(privName) {
grant.Grantor.Privilege.Name = util.PrivilegeNameForMetastore(privName) grant.Grantor.Privilege.Name = util.PrivilegeNameForMetastore(privName)
} else if _, ok := existPrivGroupMap[privName]; ok {
grant.Grantor.Privilege.Name = util.PrivilegeGroupNameForMetastore(privName)
} else { } else {
log.Ctx(ctx).Warn("failed to restore, privilege group does not exist", zap.String("group", privName)) grant.Grantor.Privilege.Name = util.PrivilegeGroupNameForMetastore(privName)
err = errors.Newf("privilege group [%s] does not exist", privName)
return err
} }
err = kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Grant) if err := kc.AlterGrant(ctx, tenant, grant, milvuspb.OperatePrivilegeType_Grant); err != nil {
if err != nil { return errors.Wrap(err, "failed to alter grant")
return err
} }
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() { for _, user := range meta.GetUsers() {
if _, ok := existUserMap[user.GetUser()]; ok { if err := kc.AlterCredential(ctx, &model.Credential{
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{
Username: user.GetUser(), Username: user.GetUser(),
EncryptedPassword: user.GetPassword(), EncryptedPassword: user.GetPassword(),
}) }); err != nil {
if err != nil { return errors.Wrap(err, "failed to alter credential")
return err
} }
needRollbackUser = append(needRollbackUser, user)
// restore user role mapping // restore user role mapping
entity := &milvuspb.UserEntity{ entity := &milvuspb.UserEntity{
Name: user.GetUser(), Name: user.GetUser(),
} }
for _, role := range user.GetRoles() { for _, role := range user.GetRoles() {
err = kc.AlterUserRole(ctx, tenant, entity, role, milvuspb.OperateUserRoleType_AddUserToRole) if err := kc.AlterUserRole(ctx, tenant, entity, role, milvuspb.OperateUserRoleType_AddUserToRole); err != nil {
if err != nil { return errors.Wrap(err, "failed to alter user role")
return err
} }
} }
} }
return nil
return err
} }
func (kc *Catalog) GetPrivilegeGroup(ctx context.Context, groupName string) (*milvuspb.PrivilegeGroupInfo, error) { 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 { for _, test := range tests {
t.Run(test.description, func(t *testing.T) { t.Run(test.description, func(t *testing.T) {
err := c.CreateCredential(ctx, &model.Credential{ err := c.AlterCredential(ctx, &model.Credential{
Username: test.user, Username: test.user,
EncryptedPassword: test.password, 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) { t.Run("test CreateRole", func(t *testing.T) {
var ( var (
kvmock = mocks.NewTxnKV(t) kvmock = mocks.NewTxnKV(t)
c = NewCatalog(kvmock, nil) 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() kvmock.EXPECT().Save(mock.Anything, mock.Anything, mock.Anything).Call.Return(nil).Once()
err := c.CreateRole(ctx, tenant, &milvuspb.RoleEntity{
tests := []struct { Name: "role1",
description string })
assert.NoError(t, err)
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)
}
})
}
}) })
t.Run("test DropRole", func(t *testing.T) { t.Run("test DropRole", func(t *testing.T) {
var ( var (
@ -1920,58 +1843,17 @@ func TestRBAC_Role(t *testing.T) {
c = NewCatalog(kvmock, nil) c = NewCatalog(kvmock, nil)
user = "default-user" 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().Save(mock.Anything, mock.Anything, mock.Anything).Return(nil)
kvmock.EXPECT().Remove(mock.Anything, mock.Anything).Return(nil) kvmock.EXPECT().Remove(mock.Anything, mock.Anything).Return(nil)
err := c.AlterUserRole(ctx, tenant, &milvuspb.UserEntity{Name: user}, &milvuspb.RoleEntity{
// Catalog.save() returns error Name: "role1",
kvmock.EXPECT().Load(mock.Anything, errorRoleSavepath).Return("", nil) }, milvuspb.OperateUserRoleType_RemoveUserFromRole)
require.NoError(t, err)
// Catalog.save() returns nil err = c.AlterUserRole(ctx, tenant, &milvuspb.UserEntity{Name: user}, &milvuspb.RoleEntity{
kvmock.EXPECT().Load(mock.Anything, noErrorRoleSavepath).Return("", merr.WrapErrIoKeyNotFound(noErrorRoleSavepath)) Name: "role1",
}, milvuspb.OperateUserRoleType_AddUserToRole)
// Catalog.remove() returns error require.NoError(t, err)
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)
}
})
}
}) })
t.Run("test ListRole", func(t *testing.T) { t.Run("test ListRole", func(t *testing.T) {
@ -2816,7 +2698,7 @@ func TestRBAC_Backup(t *testing.T) {
Privilege: &milvuspb.PrivilegeEntity{Name: "PrivilegeLoad"}, Privilege: &milvuspb.PrivilegeEntity{Name: "PrivilegeLoad"},
}, },
}, milvuspb.OperatePrivilegeType_Grant) }, milvuspb.OperatePrivilegeType_Grant)
c.CreateCredential(ctx, &model.Credential{ c.AlterCredential(ctx, &model.Credential{
Username: "user1", Username: "user1",
EncryptedPassword: "passwd", EncryptedPassword: "passwd",
}) })
@ -2940,15 +2822,6 @@ func TestRBAC_Restore(t *testing.T) {
}, },
}, },
}, },
{
User: "user1",
Password: "passwd",
Roles: []*milvuspb.RoleEntity{
{
Name: "role2",
},
},
},
}, },
Roles: []*milvuspb.RoleEntity{ Roles: []*milvuspb.RoleEntity{
{ {
@ -2979,30 +2852,31 @@ func TestRBAC_Restore(t *testing.T) {
// test restore failed and roll back // test restore failed and roll back
err = c.RestoreRBAC(ctx, util.DefaultTenant, rbacMeta2) err = c.RestoreRBAC(ctx, util.DefaultTenant, rbacMeta2)
assert.Error(t, err) assert.NoError(t, err)
// check user // check user
users, err = c.ListCredentialsWithPasswd(ctx) users, err = c.ListCredentialsWithPasswd(ctx)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, users, 1) assert.Len(t, users, 2)
// check role // check role
roles, err = c.ListRole(ctx, util.DefaultTenant, nil, false) roles, err = c.ListRole(ctx, util.DefaultTenant, nil, false)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, roles, 1) assert.Len(t, roles, 2)
// check grant // check grant
grants, err = c.ListGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{ grants, err = c.ListGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: roles[0].Role, Role: &milvuspb.RoleEntity{Name: "role2"},
DbName: util.AnyWord, DbName: util.AnyWord,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, grants, 1) 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 // check privilege group
privGroups, err = c.ListPrivilegeGroups(ctx) privGroups, err = c.ListPrivilegeGroups(ctx)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, privGroups, 1) assert.Len(t, privGroups, 2)
assert.Equal(t, "custom_group", privGroups[0].GroupName)
assert.Equal(t, "CreateCollection", privGroups[0].Privileges[0].Name)
} }
func TestRBAC_PrivilegeGroup(t *testing.T) { func TestRBAC_PrivilegeGroup(t *testing.T) {

View File

@ -8,6 +8,7 @@ type Credential struct {
Tenant string Tenant string
IsSuper bool IsSuper bool
Sha256Password string Sha256Password string
TimeTick uint64 // the timetick in wal which the credential updates
} }
func MarshalCredentialModel(cred *Credential) *internalpb.CredentialInfo { func MarshalCredentialModel(cred *Credential) *internalpb.CredentialInfo {
@ -20,5 +21,20 @@ func MarshalCredentialModel(cred *Credential) *internalpb.CredentialInfo {
EncryptedPassword: cred.EncryptedPassword, EncryptedPassword: cred.EncryptedPassword,
IsSuper: cred.IsSuper, IsSuper: cred.IsSuper,
Sha256Password: cred.Sha256Password, 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" 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/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/rootcoordpb" "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"
"github.com/milvus-io/milvus/pkg/v2/util/contextutil" "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/funcutil"
"github.com/milvus-io/milvus/pkg/v2/util/merr" "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/paramtable"
@ -48,8 +50,14 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util/typeutil" "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 //go:generate mockery --name=IMetaTable --structname=MockIMetaTable --output=./ --filename=mock_meta_table.go --with-expecter --inpackage
type IMetaTable interface { type IMetaTable interface {
MetaTableChecker
GetDatabaseByID(ctx context.Context, dbID int64, ts Timestamp) (*model.Database, error) GetDatabaseByID(ctx context.Context, dbID int64, ts Timestamp) (*model.Database, error)
GetDatabaseByName(ctx context.Context, dbName string, 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 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 IsAlias(ctx context.Context, db, name string) bool
ListAliasesByID(ctx context.Context, collID UniqueID) []string ListAliasesByID(ctx context.Context, collID UniqueID) []string
AddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error
GetCredential(ctx context.Context, username string) (*internalpb.CredentialInfo, error) GetCredential(ctx context.Context, username string) (*internalpb.CredentialInfo, error)
DeleteCredential(ctx context.Context, username string) error InitCredential(ctx context.Context) error
AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error DeleteCredential(ctx context.Context, result message.BroadcastResultDropUserMessageV2) error
AlterCredential(ctx context.Context, result message.BroadcastResultAlterUserMessageV2) error
ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error)
CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) 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 return mt.generalCnt
} }
// AddCredential add credential func (mt *MetaTable) InitCredential(ctx context.Context) error {
func (mt *MetaTable) AddCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
if credInfo.Username == "" {
return errors.New("username is empty")
}
mt.permissionLock.Lock() mt.permissionLock.Lock()
defer mt.permissionLock.Unlock() 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) usernames, err := mt.catalog.ListCredentials(ctx)
if err != nil { if err != nil {
return err 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" 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 errors.New(errMsg)
} }
return nil
}
if origin, _ := mt.catalog.GetCredential(ctx, credInfo.Username); origin != nil { func (mt *MetaTable) CheckIfUpdateCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error {
return fmt.Errorf("user already exists: %s", credInfo.Username) if funcutil.IsEmptyString(credInfo.GetUsername()) {
return errEmptyUsername
} }
mt.permissionLock.RLock()
defer mt.permissionLock.RUnlock()
credential := &model.Credential{ // check if the number of credential exists.
Username: credInfo.Username, if _, err := mt.catalog.GetCredential(ctx, credInfo.GetUsername()); err != nil {
EncryptedPassword: credInfo.EncryptedPassword, if errors.Is(err, merr.ErrIoKeyNotFound) {
return errUserNotFound
}
return err
} }
return mt.catalog.CreateCredential(ctx, credential) return nil
} }
// AlterCredential update credential // AlterCredential update credential
func (mt *MetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error { func (mt *MetaTable) AlterCredential(ctx context.Context, result message.BroadcastResultAlterUserMessageV2) error {
if credInfo.Username == "" { body := result.Message.MustBody()
return errors.New("username is empty")
}
mt.permissionLock.Lock() mt.permissionLock.Lock()
defer mt.permissionLock.Unlock() 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{ credential := &model.Credential{
Username: credInfo.Username, Username: body.CredentialInfo.Username,
EncryptedPassword: credInfo.EncryptedPassword, EncryptedPassword: body.CredentialInfo.EncryptedPassword,
TimeTick: result.GetControlChannelResult().TimeTick,
} }
return mt.catalog.AlterCredential(ctx, credential) 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 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 // 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() mt.permissionLock.Lock()
defer mt.permissionLock.Unlock() 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 // ListCredentialUsernames list credential usernames
@ -1427,23 +1521,23 @@ func (mt *MetaTable) ListCredentialUsernames(ctx context.Context) (*milvuspb.Lis
return &milvuspb.ListCredUsersResponse{Usernames: usernames}, nil return &milvuspb.ListCredUsersResponse{Usernames: usernames}, nil
} }
// CreateRole create role // CheckIfCreateRole checks if the role can be created.
func (mt *MetaTable) CreateRole(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error { func (mt *MetaTable) CheckIfCreateRole(ctx context.Context, in *milvuspb.CreateRoleRequest) error {
if funcutil.IsEmptyString(entity.Name) { if funcutil.IsEmptyString(in.GetEntity().GetName()) {
return errors.New("the role name in the role info is empty") return errEmptyRoleName
} }
mt.permissionLock.Lock() mt.permissionLock.RLock()
defer mt.permissionLock.Unlock() 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 { if err != nil {
log.Ctx(ctx).Warn("fail to list roles", zap.Error(err)) log.Ctx(ctx).Warn("fail to list roles", zap.Error(err))
return err return err
} }
for _, result := range results { for _, result := range results {
if result.GetRole().GetName() == entity.Name { if result.GetRole().GetName() == in.GetEntity().GetName() {
log.Ctx(ctx).Info("role already exists", zap.String("role", entity.Name)) log.Ctx(ctx).Info("role already exists", zap.String("role", in.GetEntity().GetName()))
return common.NewIgnorableError(errors.Newf("role [%s] already exists", entity)) return errRoleAlreadyExists
} }
} }
if len(results) >= Params.ProxyCfg.MaxRoleNum.GetAsInt() { 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())) log.Ctx(ctx).Warn(errMsg, zap.Int("max_role_num", Params.ProxyCfg.MaxRoleNum.GetAsInt()))
return errors.New(errMsg) 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) 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 // DropRole drop role info
func (mt *MetaTable) DropRole(ctx context.Context, tenant string, roleName string) error { func (mt *MetaTable) DropRole(ctx context.Context, tenant string, roleName string) error {
mt.permissionLock.Lock() 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) 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) CheckIfOperateUserRole(ctx context.Context, req *milvuspb.OperateUserRoleRequest) error {
func (mt *MetaTable) OperateUserRole(ctx context.Context, tenant string, userEntity *milvuspb.UserEntity, roleEntity *milvuspb.RoleEntity, operateType milvuspb.OperateUserRoleType) error { if funcutil.IsEmptyString(req.GetUsername()) {
if funcutil.IsEmptyString(userEntity.Name) {
return errors.New("username in the user entity is empty") 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") 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() mt.permissionLock.Lock()
defer mt.permissionLock.Unlock() 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) 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 { func (mt *MetaTable) RestoreRBAC(ctx context.Context, tenant string, meta *milvuspb.RBACMeta) error {
mt.permissionLock.Lock() mt.permissionLock.Lock()
defer mt.permissionLock.Unlock() defer mt.permissionLock.Unlock()
@ -1605,23 +1824,30 @@ func (mt *MetaTable) IsCustomPrivilegeGroup(ctx context.Context, groupName strin
return false, nil return false, nil
} }
func (mt *MetaTable) CreatePrivilegeGroup(ctx context.Context, groupName string) error { func (mt *MetaTable) CheckIfPrivilegeGroupCreatable(ctx context.Context, req *milvuspb.CreatePrivilegeGroupRequest) error {
if funcutil.IsEmptyString(groupName) { if funcutil.IsEmptyString(req.GetGroupName()) {
return errors.New("the privilege group name is empty") return errEmptyPrivilegeGroupName
} }
mt.permissionLock.Lock() mt.permissionLock.RLock()
defer mt.permissionLock.Unlock() defer mt.permissionLock.RUnlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, groupName) definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, req.GetGroupName())
if err != nil { if err != nil {
return err return err
} }
if definedByUsers { 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) { if util.IsPrivilegeNameDefined(req.GetGroupName()) {
return merr.WrapErrParameterInvalidMsg("privilege group name [%s] is defined by built in privileges or privilege groups in system", groupName) 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{ data := &milvuspb.PrivilegeGroupInfo{
GroupName: groupName, GroupName: groupName,
Privileges: make([]*milvuspb.PrivilegeEntity, 0), Privileges: make([]*milvuspb.PrivilegeEntity, 0),
@ -1629,20 +1855,21 @@ func (mt *MetaTable) CreatePrivilegeGroup(ctx context.Context, groupName string)
return mt.catalog.SavePrivilegeGroup(ctx, data) return mt.catalog.SavePrivilegeGroup(ctx, data)
} }
func (mt *MetaTable) DropPrivilegeGroup(ctx context.Context, groupName string) error { func (mt *MetaTable) CheckIfPrivilegeGroupDropable(ctx context.Context, req *milvuspb.DropPrivilegeGroupRequest) error {
if funcutil.IsEmptyString(groupName) { if funcutil.IsEmptyString(req.GetGroupName()) {
return errors.New("the privilege group name is empty") return errEmptyPrivilegeGroupName
} }
mt.permissionLock.Lock() mt.permissionLock.RLock()
defer mt.permissionLock.Unlock() defer mt.permissionLock.RUnlock()
definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, groupName) definedByUsers, err := mt.IsCustomPrivilegeGroup(ctx, req.GetGroupName())
if err != nil { if err != nil {
return err return err
} }
if !definedByUsers { if !definedByUsers {
return nil return errNotCustomPrivilegeGroup
} }
// check if the group is used by any role // check if the group is used by any role
roles, err := mt.catalog.ListRole(ctx, util.DefaultTenant, nil, false) roles, err := mt.catalog.ListRole(ctx, util.DefaultTenant, nil, false)
if err != nil { if err != nil {
@ -1660,11 +1887,18 @@ func (mt *MetaTable) DropPrivilegeGroup(ctx context.Context, groupName string) e
return err return err
} }
for _, grant := range grants { for _, grant := range grants {
if grant.Grantor.Privilege.Name == groupName { 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", groupName, role.GetName()) 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) return mt.catalog.DropPrivilegeGroup(ctx, groupName)
} }
@ -1675,43 +1909,55 @@ func (mt *MetaTable) ListPrivilegeGroups(ctx context.Context) ([]*milvuspb.Privi
return mt.catalog.ListPrivilegeGroups(ctx) return mt.catalog.ListPrivilegeGroups(ctx)
} }
func (mt *MetaTable) OperatePrivilegeGroup(ctx context.Context, groupName string, privileges []*milvuspb.PrivilegeEntity, operateType milvuspb.OperatePrivilegeGroupType) error { // CheckIfPrivilegeGroupAlterable checks if the privilege group can be altered.
if funcutil.IsEmptyString(groupName) { func (mt *MetaTable) CheckIfPrivilegeGroupAlterable(ctx context.Context, req *milvuspb.OperatePrivilegeGroupRequest) error {
return errors.New("the privilege group name is empty") if funcutil.IsEmptyString(req.GetGroupName()) {
return errEmptyPrivilegeGroupName
} }
mt.permissionLock.Lock() mt.permissionLock.RLock()
defer mt.permissionLock.Unlock() 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) groups, err := mt.catalog.ListPrivilegeGroups(ctx)
if err != nil { if err != nil {
return err 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) { if util.IsPrivilegeNameDefined(p.Name) {
continue continue
} }
for _, group := range groups { if _, ok := currenctGroups[p.Name]; !ok {
// add privileges for custom privilege group return merr.WrapErrParameterInvalidMsg("there is no privilege name or privilege group name [%s] defined in system to operate", p.Name)
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 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 // merge with current privileges
group, err := mt.catalog.GetPrivilegeGroup(ctx, groupName) group, err := mt.catalog.GetPrivilegeGroup(ctx, groupName)
if err != nil { 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/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb" "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/kv/rootcoord"
"github.com/milvus-io/milvus/internal/metastore/mocks" "github.com/milvus-io/milvus/internal/metastore/mocks"
"github.com/milvus-io/milvus/internal/metastore/model" "github.com/milvus-io/milvus/internal/metastore/model"
"github.com/milvus-io/milvus/internal/streamingcoord/server/balancer/channel" "github.com/milvus-io/milvus/internal/streamingcoord/server/balancer/channel"
mocktso "github.com/milvus-io/milvus/internal/tso/mocks" 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" 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/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"
"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/merr"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable" "github.com/milvus-io/milvus/pkg/v2/util/paramtable"
"github.com/milvus-io/milvus/pkg/v2/util/typeutil" "github.com/milvus-io/milvus/pkg/v2/util/typeutil"
) )
func generateMetaTable(t *testing.T) *MetaTable { func generateMetaTable(_ *testing.T) *MetaTable {
return &MetaTable{catalog: rootcoord.NewCatalog(memkv.NewMemoryKV(), nil)} 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) 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, 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) require.NoError(t, err)
tests := []struct { tests := []struct {
@ -63,7 +112,7 @@ func TestRbacAddCredential(t *testing.T) {
}{ }{
{"Empty username", false, &internalpb.CredentialInfo{Username: ""}}, {"Empty username", false, &internalpb.CredentialInfo{Username: ""}},
{"exceed MaxUserNum", true, &internalpb.CredentialInfo{Username: "user3", Tenant: util.DefaultTenant}}, {"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 { for _, test := range tests {
@ -74,10 +123,39 @@ func TestRbacAddCredential(t *testing.T) {
paramtable.Get().Save(Params.ProxyCfg.MaxUserNum.Key, "3") paramtable.Get().Save(Params.ProxyCfg.MaxUserNum.Key, "3")
} }
defer paramtable.Get().Reset(Params.ProxyCfg.MaxUserNum.Key) 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) 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) { func TestRbacCreateRole(t *testing.T) {
@ -85,7 +163,11 @@ func TestRbacCreateRole(t *testing.T) {
paramtable.Get().Save(Params.ProxyCfg.MaxRoleNum.Key, "2") paramtable.Get().Save(Params.ProxyCfg.MaxRoleNum.Key, "2")
defer paramtable.Get().Reset(Params.ProxyCfg.MaxRoleNum.Key) 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) require.NoError(t, err)
err = mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role2"}) err = mt.CreateRole(context.TODO(), util.DefaultTenant, &milvuspb.RoleEntity{Name: "role2"})
require.NoError(t, err) require.NoError(t, err)
@ -101,14 +183,14 @@ func TestRbacCreateRole(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.description, func(t *testing.T) { 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) assert.Error(t, err)
}) })
} }
t.Run("role has existed", func(t *testing.T) { 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.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, mock.Anything,
).Return(nil, errors.New("error mock list role")) ).Return(nil, errors.New("error mock list role"))
mockMt := &MetaTable{catalog: mockCata} 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) assert.Error(t, err)
} }
} }
@ -128,24 +210,21 @@ func TestRbacCreateRole(t *testing.T) {
func TestRbacDropRole(t *testing.T) { func TestRbacDropRole(t *testing.T) {
mt := generateMetaTable(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) require.NoError(t, err)
tests := []struct { // drop a not exist role
roleName string err = mt.CheckIfDropRole(context.TODO(), &milvuspb.DropRoleRequest{RoleName: "role_not_exist"})
require.ErrorIs(t, err, errRoleNotExists)
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)
})
}
} }
func TestRbacOperateRole(t *testing.T) { func TestRbacOperateRole(t *testing.T) {
@ -169,7 +248,11 @@ func TestRbacOperateRole(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.description, func(t *testing.T) { 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) assert.Error(t, err)
}) })
} }
@ -191,7 +274,7 @@ func TestRbacSelect(t *testing.T) {
} }
for user, rs := range userRoles { for user, rs := range userRoles {
err := mt.catalog.CreateCredential(context.TODO(), &model.Credential{ err := mt.catalog.AlterCredential(context.TODO(), &model.Credential{
Username: user, Username: user,
Tenant: util.DefaultTenant, Tenant: util.DefaultTenant,
}) })
@ -2226,23 +2309,55 @@ func TestMetaTable_PrivilegeGroup(t *testing.T) {
aliases: newNameDb(), aliases: newNameDb(),
catalog: catalog, catalog: catalog,
} }
err := mt.CreatePrivilegeGroup(context.TODO(), "pg1") err := mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "pg1",
})
assert.Error(t, err) assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "") err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "",
})
assert.Error(t, err) assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "Insert") err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "Insert",
})
assert.Error(t, err) assert.Error(t, err)
err = mt.CreatePrivilegeGroup(context.TODO(), "pg2") err = mt.CheckIfPrivilegeGroupCreatable(context.TODO(), &milvuspb.CreatePrivilegeGroupRequest{
GroupName: "pg2",
})
assert.NoError(t, err) 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) assert.Error(t, err)
err = mt.CheckIfPrivilegeGroupDropable(context.TODO(), &milvuspb.DropPrivilegeGroupRequest{
GroupName: "pg1",
})
assert.NoError(t, err)
err = mt.DropPrivilegeGroup(context.TODO(), "pg1") err = mt.DropPrivilegeGroup(context.TODO(), "pg1")
assert.NoError(t, err) 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) 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) 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) assert.Error(t, err)
_, err = mt.GetPrivilegeGroupRoles(context.TODO(), "") _, err = mt.GetPrivilegeGroupRoles(context.TODO(), "")
assert.Error(t, err) 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/internalpb"
"github.com/milvus-io/milvus/pkg/v2/proto/proxypb" "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/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/merr"
"github.com/milvus-io/milvus/pkg/v2/util/metricsinfo" "github.com/milvus-io/milvus/pkg/v2/util/metricsinfo"
"github.com/milvus-io/milvus/pkg/v2/util/paramtable" "github.com/milvus-io/milvus/pkg/v2/util/paramtable"
@ -56,6 +57,8 @@ const (
TestRootCoordID = 200 TestRootCoordID = 200
) )
var _ IMetaTable = &mockMetaTable{}
// TODO: remove mockMetaTable, use mockery instead // TODO: remove mockMetaTable, use mockery instead
type mockMetaTable struct { type mockMetaTable struct {
IMetaTable IMetaTable
@ -83,8 +86,8 @@ type mockMetaTable struct {
RenameCollectionFunc func(ctx context.Context, oldName string, newName string, ts Timestamp) error RenameCollectionFunc func(ctx context.Context, oldName string, newName string, ts Timestamp) error
AddCredentialFunc func(ctx context.Context, credInfo *internalpb.CredentialInfo) error AddCredentialFunc func(ctx context.Context, credInfo *internalpb.CredentialInfo) error
GetCredentialFunc func(ctx context.Context, username string) (*internalpb.CredentialInfo, error) GetCredentialFunc func(ctx context.Context, username string) (*internalpb.CredentialInfo, error)
DeleteCredentialFunc func(ctx context.Context, username string) error DeleteCredentialFunc func(ctx context.Context, msg message.BroadcastResultDropUserMessageV2) error
AlterCredentialFunc func(ctx context.Context, credInfo *internalpb.CredentialInfo) error AlterCredentialFunc func(ctx context.Context, msg message.BroadcastResultAlterUserMessageV2) error
ListCredentialUsernamesFunc func(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) ListCredentialUsernamesFunc func(ctx context.Context) (*milvuspb.ListCredUsersResponse, error)
CreateRoleFunc func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error CreateRoleFunc func(ctx context.Context, tenant string, entity *milvuspb.RoleEntity) error
DropRoleFunc func(ctx context.Context, tenant string, roleName string) 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) return m.GetCredentialFunc(ctx, username)
} }
func (m mockMetaTable) DeleteCredential(ctx context.Context, username string) error { func (m mockMetaTable) DeleteCredential(ctx context.Context, msg message.BroadcastResultDropUserMessageV2) error {
return m.DeleteCredentialFunc(ctx, username) return m.DeleteCredentialFunc(ctx, msg)
} }
func (m mockMetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error { func (m mockMetaTable) AlterCredential(ctx context.Context, msg message.BroadcastResultAlterUserMessageV2) error {
return m.AlterCredentialFunc(ctx, credInfo) return m.AlterCredentialFunc(ctx, msg)
} }
func (m mockMetaTable) ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) { func (m mockMetaTable) ListCredentialUsernames(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) {
@ -373,12 +376,17 @@ func newMockTsoAllocator() *tso.MockAllocator {
type mockProxy struct { type mockProxy struct {
types.ProxyClient types.ProxyClient
UpdateCredentialCacheFunc func(ctx context.Context, request *proxypb.UpdateCredCacheRequest) (*commonpb.Status, error)
InvalidateCollectionMetaCacheFunc func(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest) (*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) InvalidateCredentialCacheFunc func(ctx context.Context, request *proxypb.InvalidateCredCacheRequest) (*commonpb.Status, error)
RefreshPolicyInfoCacheFunc func(ctx context.Context, request *proxypb.RefreshPolicyInfoCacheRequest) (*commonpb.Status, error) RefreshPolicyInfoCacheFunc func(ctx context.Context, request *proxypb.RefreshPolicyInfoCacheRequest) (*commonpb.Status, error)
GetComponentStatesFunc func(ctx context.Context) (*milvuspb.ComponentStates, 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) { func (m mockProxy) InvalidateCollectionMetaCache(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest, opts ...grpc.CallOption) (*commonpb.Status, error) {
return m.InvalidateCollectionMetaCacheFunc(ctx, request) return m.InvalidateCollectionMetaCacheFunc(ctx, request)
} }
@ -426,9 +434,18 @@ func withValidProxyManager() Opt {
return func(c *Core) { return func(c *Core) {
c.proxyClientManager = proxyutil.NewProxyClientManager(proxyutil.DefaultProxyCreator) c.proxyClientManager = proxyutil.NewProxyClientManager(proxyutil.DefaultProxyCreator)
p := newMockProxy() 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) { p.InvalidateCollectionMetaCacheFunc = func(ctx context.Context, request *proxypb.InvalidateCollMetaCacheRequest) (*commonpb.Status, error) {
return merr.Success(), nil 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) { p.GetComponentStatesFunc = func(ctx context.Context) (*milvuspb.ComponentStates, error) {
return &milvuspb.ComponentStates{ return &milvuspb.ComponentStates{
State: &milvuspb.ComponentInfo{StateCode: commonpb.StateCode_Healthy}, 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) { meta.GetCredentialFunc = func(ctx context.Context, username string) (*internalpb.CredentialInfo, error) {
return nil, errors.New("error mock GetCredential") 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") 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") return errors.New("error mock AlterCredential")
} }
meta.ListCredentialUsernamesFunc = func(ctx context.Context) (*milvuspb.ListCredUsersResponse, error) { 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" etcdpb "github.com/milvus-io/milvus/pkg/v2/proto/etcdpb"
internalpb "github.com/milvus-io/milvus/pkg/v2/proto/internalpb" 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" milvuspb "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
@ -77,53 +81,6 @@ func (_c *IMetaTable_AddCollection_Call) RunAndReturn(run func(context.Context,
return _c 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 // AddPartition provides a mock function with given fields: ctx, partition
func (_m *IMetaTable) AddPartition(ctx context.Context, partition *model.Partition) error { func (_m *IMetaTable) AddPartition(ctx context.Context, partition *model.Partition) error {
ret := _m.Called(ctx, partition) ret := _m.Called(ctx, partition)
@ -271,17 +228,17 @@ func (_c *IMetaTable_AlterCollection_Call) RunAndReturn(run func(context.Context
return _c return _c
} }
// AlterCredential provides a mock function with given fields: ctx, credInfo // AlterCredential provides a mock function with given fields: ctx, result
func (_m *IMetaTable) AlterCredential(ctx context.Context, credInfo *internalpb.CredentialInfo) error { func (_m *IMetaTable) AlterCredential(ctx context.Context, result message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]) error {
ret := _m.Called(ctx, credInfo) ret := _m.Called(ctx, result)
if len(ret) == 0 { if len(ret) == 0 {
panic("no return value specified for AlterCredential") panic("no return value specified for AlterCredential")
} }
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, *internalpb.CredentialInfo) error); ok { if rf, ok := ret.Get(0).(func(context.Context, message.BroadcastResult[*messagespb.AlterUserMessageHeader, *messagespb.AlterUserMessageBody]) error); ok {
r0 = rf(ctx, credInfo) r0 = rf(ctx, result)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@ -296,14 +253,14 @@ type IMetaTable_AlterCredential_Call struct {
// AlterCredential is a helper method to define mock.On call // AlterCredential is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - credInfo *internalpb.CredentialInfo // - result message.BroadcastResult[*messagespb.AlterUserMessageHeader,*messagespb.AlterUserMessageBody]
func (_e *IMetaTable_Expecter) AlterCredential(ctx interface{}, credInfo interface{}) *IMetaTable_AlterCredential_Call { func (_e *IMetaTable_Expecter) AlterCredential(ctx interface{}, result interface{}) *IMetaTable_AlterCredential_Call {
return &IMetaTable_AlterCredential_Call{Call: _e.mock.On("AlterCredential", ctx, credInfo)} 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) { _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 return _c
} }
@ -313,7 +270,7 @@ func (_c *IMetaTable_AlterCredential_Call) Return(_a0 error) *IMetaTable_AlterCr
return _c 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) _c.Call.Return(run)
return _c return _c
} }
@ -525,6 +482,476 @@ func (_c *IMetaTable_ChangePartitionState_Call) RunAndReturn(run func(context.Co
return _c 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 // 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 { func (_m *IMetaTable) CreateAlias(ctx context.Context, dbName string, alias string, collectionName string, ts uint64) error {
ret := _m.Called(ctx, dbName, alias, collectionName, ts) 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 return _c
} }
// DeleteCredential provides a mock function with given fields: ctx, username // DeleteCredential provides a mock function with given fields: ctx, result
func (_m *IMetaTable) DeleteCredential(ctx context.Context, username string) error { func (_m *IMetaTable) DeleteCredential(ctx context.Context, result message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]) error {
ret := _m.Called(ctx, username) ret := _m.Called(ctx, result)
if len(ret) == 0 { if len(ret) == 0 {
panic("no return value specified for DeleteCredential") panic("no return value specified for DeleteCredential")
} }
var r0 error var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { if rf, ok := ret.Get(0).(func(context.Context, message.BroadcastResult[*messagespb.DropUserMessageHeader, *messagespb.DropUserMessageBody]) error); ok {
r0 = rf(ctx, username) r0 = rf(ctx, result)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)
} }
@ -743,14 +1170,14 @@ type IMetaTable_DeleteCredential_Call struct {
// DeleteCredential is a helper method to define mock.On call // DeleteCredential is a helper method to define mock.On call
// - ctx context.Context // - ctx context.Context
// - username string // - result message.BroadcastResult[*messagespb.DropUserMessageHeader,*messagespb.DropUserMessageBody]
func (_e *IMetaTable_Expecter) DeleteCredential(ctx interface{}, username interface{}) *IMetaTable_DeleteCredential_Call { func (_e *IMetaTable_Expecter) DeleteCredential(ctx interface{}, result interface{}) *IMetaTable_DeleteCredential_Call {
return &IMetaTable_DeleteCredential_Call{Call: _e.mock.On("DeleteCredential", ctx, username)} 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) { _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 return _c
} }
@ -760,7 +1187,7 @@ func (_c *IMetaTable_DeleteCredential_Call) Return(_a0 error) *IMetaTable_Delete
return _c 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) _c.Call.Return(run)
return _c return _c
} }
@ -1676,6 +2103,52 @@ func (_c *IMetaTable_GetPrivilegeGroupRoles_Call) RunAndReturn(run func(context.
return _c 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 // IsAlias provides a mock function with given fields: ctx, db, name
func (_m *IMetaTable) IsAlias(ctx context.Context, db string, name string) bool { func (_m *IMetaTable) IsAlias(ctx context.Context, db string, name string) bool {
ret := _m.Called(ctx, db, name) ret := _m.Called(ctx, db, name)

View File

@ -35,171 +35,64 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util/typeutil" "github.com/milvus-io/milvus/pkg/v2/util/typeutil"
) )
func executeDeleteCredentialTaskSteps(ctx context.Context, core *Core, username string) error { func executeOperatePrivilegeTaskSteps(ctx context.Context, core *Core, entity *milvuspb.GrantEntity, operateType milvuspb.OperatePrivilegeType) error {
redoTask := newBaseRedoTask(core.stepExecutor) privName := entity.Grantor.Privilege.Name
redoTask.AddSyncStep(NewSimpleStep("delete credential meta data", func(ctx context.Context) ([]nestedStep, error) { if err := func() 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) {
// set up privilege name for metastore // set up privilege name for metastore
dbPrivName, err := core.getMetastorePrivilegeName(ctx, privName) dbPrivName, err := core.getMetastorePrivilegeName(ctx, privName)
if err != nil { 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) { if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to operate the privilege", zap.Any("in", in), zap.Error(err)) log.Ctx(ctx).Warn("fail to operate the privilege", zap.Any("in", entity), zap.Error(err))
return nil, err return err
} }
return nil, nil return nil
})) }(); err != nil {
redoTask.AddAsyncStep(NewSimpleStep("operate privilege cache", func(ctx context.Context) ([]nestedStep, error) { return errors.Wrap(err, "failed to operate the privilege")
}
if err := func() error {
// set back to expand privilege group // set back to expand privilege group
in.Entity.Grantor.Privilege.Name = privName entity.Grantor.Privilege.Name = privName
var opType int32 var opType int32
switch in.Type { switch operateType {
case milvuspb.OperatePrivilegeType_Grant: case milvuspb.OperatePrivilegeType_Grant:
opType = int32(typeutil.CacheGrantPrivilege) opType = int32(typeutil.CacheGrantPrivilege)
case milvuspb.OperatePrivilegeType_Revoke: case milvuspb.OperatePrivilegeType_Revoke:
opType = int32(typeutil.CacheRevokePrivilege) opType = int32(typeutil.CacheRevokePrivilege)
default: default:
log.Ctx(ctx).Warn("invalid operate type for the OperatePrivilege api", zap.Any("in", in)) log.Ctx(ctx).Warn("invalid operate type for the OperatePrivilege api", zap.Any("operate_type", operateType))
return nil, nil return errors.New("invalid operate type for the OperatePrivilege api")
} }
grants := []*milvuspb.GrantEntity{in.Entity} grants := []*milvuspb.GrantEntity{entity}
allGroups, err := core.getDefaultAndCustomPrivilegeGroups(ctx) allGroups, err := core.getDefaultAndCustomPrivilegeGroups(ctx)
if err != nil { if err != nil {
return nil, err return err
} }
groups := lo.SliceToMap(allGroups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) { groups := lo.SliceToMap(allGroups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges return group.GroupName, group.Privileges
}) })
expandGrants, err := core.expandPrivilegeGroups(ctx, grants, groups) expandGrants, err := core.expandPrivilegeGroups(ctx, grants, groups)
if err != nil { 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 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{ metaGrants, err := core.meta.SelectGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{
Role: in.Entity.Role, Role: entity.Role,
DbName: in.Entity.DbName, DbName: entity.DbName,
}) })
if err != nil { if err != nil {
return nil, err return err
} }
metaExpandGrants, err := core.expandPrivilegeGroups(ctx, metaGrants, groups) metaExpandGrants, err := core.expandPrivilegeGroups(ctx, metaGrants, groups)
if err != nil { if err != nil {
return nil, err return err
} }
expandGrants = lo.Filter(expandGrants, func(g1 *milvuspb.GrantEntity, _ int) bool { expandGrants = lo.Filter(expandGrants, func(g1 *milvuspb.GrantEntity, _ int) bool {
return !lo.ContainsBy(metaExpandGrants, func(g2 *milvuspb.GrantEntity) 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, OpType: opType,
OpKey: funcutil.PolicyForPrivileges(expandGrants), OpKey: funcutil.PolicyForPrivileges(expandGrants),
}); err != nil { }); err != nil {
log.Ctx(ctx).Warn("fail to refresh policy info cache", zap.Any("in", in), zap.Error(err)) log.Ctx(ctx).Warn("fail to refresh policy info cache", zap.Any("in", entity), zap.Error(err))
return nil, err return err
} }
} }
return nil, nil return nil
})) }(); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
return redoTask.Execute(ctx) }
return nil
} }
func executeRestoreRBACTaskSteps(ctx context.Context, core *Core, in *milvuspb.RestoreRBACMetaRequest) error { func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *milvuspb.PrivilegeGroupInfo, operateType milvuspb.OperatePrivilegeGroupType) error {
redoTask := newBaseRedoTask(core.stepExecutor) if err := func() error {
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) {
groups, err := core.meta.ListPrivilegeGroups(ctx) groups, err := core.meta.ListPrivilegeGroups(ctx)
if err != nil && !common.IsIgnorableError(err) { if err != nil && !common.IsIgnorableError(err) {
log.Ctx(ctx).Warn("fail to list privilege groups", zap.Error(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) { currGroups := lo.SliceToMap(groups, func(group *milvuspb.PrivilegeGroupInfo) (string, []*milvuspb.PrivilegeEntity) {
return group.GroupName, group.Privileges return group.GroupName, group.Privileges
@ -259,34 +130,25 @@ func executeOperatePrivilegeGroupTaskSteps(ctx context.Context, core *Core, in *
// get roles granted to the group // get roles granted to the group
roles, err := core.meta.GetPrivilegeGroupRoles(ctx, in.GroupName) roles, err := core.meta.GetPrivilegeGroupRoles(ctx, in.GroupName)
if err != nil { if err != nil {
return nil, err return err
} }
newGroups := make(map[string][]*milvuspb.PrivilegeEntity) newGroups := make(map[string][]*milvuspb.PrivilegeEntity)
for k, v := range currGroups { for k, v := range currGroups {
if k != in.GroupName { if k != in.GroupName {
newGroups[k] = v newGroups[k] = v
continue continue
} }
switch in.Type { switch operateType {
case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup: case milvuspb.OperatePrivilegeGroupType_AddPrivilegesToGroup:
newPrivs := lo.Union(v, in.Privileges) newPrivs := lo.Union(v, in.Privileges)
newGroups[k] = lo.UniqBy(newPrivs, func(p *milvuspb.PrivilegeEntity) string { newGroups[k] = lo.UniqBy(newPrivs, func(p *milvuspb.PrivilegeEntity) string {
return p.Name 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: case milvuspb.OperatePrivilegeGroupType_RemovePrivilegesFromGroup:
newPrivs, _ := lo.Difference(v, in.Privileges) newPrivs, _ := lo.Difference(v, in.Privileges)
newGroups[k] = newPrivs newGroups[k] = newPrivs
default: 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, DbName: util.AnyWord,
}) })
if err != nil { if err != nil {
return nil, err return err
} }
currGrants, err := core.expandPrivilegeGroups(ctx, grants, currGroups) currGrants, err := core.expandPrivilegeGroups(ctx, grants, currGroups)
if err != nil { if err != nil {
return nil, err return err
} }
newGrants, err := core.expandPrivilegeGroups(ctx, grants, newGroups) newGrants, err := core.expandPrivilegeGroups(ctx, grants, newGroups)
if err != nil { if err != nil {
return nil, err return err
} }
toRevoke := lo.Filter(currGrants, func(item *milvuspb.GrantEntity, _ int) bool { 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), OpKey: funcutil.PolicyForPrivileges(rolesToRevoke),
}); err != nil { }); 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)) 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), OpKey: funcutil.PolicyForPrivileges(rolesToGrant),
}); err != nil { }); 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)) 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 return nil
})) }(); err != nil {
return errors.Wrap(err, "failed to refresh policy info cache")
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 := core.meta.OperatePrivilegeGroup(ctx, in.GroupName, in.Privileges, operateType); err != nil && !common.IsIgnorableError(err) {
if err != nil && !common.IsIgnorableError(err) { log.Ctx(ctx).Warn("fail to operate privilege group", zap.Error(err))
log.Ctx(ctx).Warn("fail to operate privilege group", zap.Error(err)) return errors.Wrap(err, "failed to operate privilege group")
} }
return nil, err return nil
}))
return redoTask.Execute(ctx)
} }

View File

@ -62,7 +62,6 @@ import (
"github.com/milvus-io/milvus/pkg/v2/util" "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/commonpbutil"
"github.com/milvus-io/milvus/pkg/v2/util/contextutil" "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/expr"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil" "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/merr"
@ -488,6 +487,7 @@ func (c *Core) Init() error {
c.initOnce.Do(func() { c.initOnce.Do(func() {
initError = c.initInternal() initError = c.initInternal()
RegisterDDLCallbacks(c)
}) })
log.Info("RootCoord init successfully") log.Info("RootCoord init successfully")
@ -495,22 +495,7 @@ func (c *Core) Init() error {
} }
func (c *Core) initCredentials(initCtx context.Context) error { func (c *Core) initCredentials(initCtx context.Context) error {
credInfo, _ := c.meta.GetCredential(initCtx, util.UserRoot) return c.meta.InitCredential(initCtx)
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
} }
func (c *Core) initRbac(initCtx context.Context) error { 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() { if Params.RoleCfg.Enabled.GetAsBool() {
return c.initBuiltinRoles() return c.initBuiltinRoles(initCtx)
} }
return nil return nil
} }
@ -580,22 +565,22 @@ func (c *Core) initPublicRolePrivilege(initCtx context.Context) error {
return nil return nil
} }
func (c *Core) initBuiltinRoles() error { func (c *Core) initBuiltinRoles(ctx context.Context) error {
log := log.Ctx(c.ctx) log := log.Ctx(ctx)
rolePrivilegesMap := Params.RoleCfg.Roles.GetAsRoleDetails() rolePrivilegesMap := Params.RoleCfg.Roles.GetAsRoleDetails()
for role, privilegesJSON := range rolePrivilegesMap { 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) { if err != nil && !common.IsIgnorableError(err) {
log.Error("create a builtin role fail", zap.String("roleName", role), zap.Error(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) return errors.Wrapf(err, "failed to create a builtin role: %s", role)
} }
for _, privilege := range privilegesJSON[util.RoleConfigPrivileges] { 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 { if err != nil {
return errors.Wrapf(err, "failed to get metastore privilege name for: %s", privilege[util.RoleConfigPrivilege]) 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}, Role: &milvuspb.RoleEntity{Name: role},
Object: &milvuspb.ObjectEntity{Name: privilege[util.RoleConfigObjectType]}, Object: &milvuspb.ObjectEntity{Name: privilege[util.RoleConfigObjectType]},
ObjectName: privilege[util.RoleConfigObjectName], ObjectName: privilege[util.RoleConfigObjectName],
@ -2193,20 +2178,14 @@ func (c *Core) CreateCredential(ctx context.Context, credInfo *internalpb.Creden
return merr.Status(err), nil return merr.Status(err), nil
} }
// insert to db if err := c.broadcastAlterUserForCreateCredential(ctx, credInfo); err != nil {
err := c.meta.AddCredential(ctx, credInfo) ctxLog.Warn("CreateCredential failed", zap.Error(err))
if err != nil {
ctxLog.Warn("CreateCredential save credential failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc() 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 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.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) 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 { if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil return merr.Status(err), nil
} }
// update data on storage
err := c.meta.AlterCredential(ctx, credInfo) if err := c.broadcastAlterUserForUpdateCredential(ctx, credInfo); err != nil {
if err != nil { ctxLog.Warn("UpdateCredential append message failed", zap.Error(err))
ctxLog.Warn("UpdateCredential save credential failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc() metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_UpdateCredentialFailure), nil 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.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) 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 { if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil return merr.Status(err), nil
} }
var status *commonpb.Status
defer func() { // user not found will not report to the client to achieve idempotent
if status.Code != 0 { if err := c.broadcastDropUserForDeleteCredential(ctx, in); err != nil {
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc() 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
} }
}() ctxLog.Warn("DeleteCredential append message failed", zap.Error(err))
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
err := executeDeleteCredentialTaskSteps(ctx, c, in.Username) return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DeleteCredentialFailure), nil
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.Debug("DeleteCredential success")
ctxLog.Debug("DeleteCredential success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc() metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfCredentials.Dec() metrics.RootCoordNumOfCredentials.Dec()
status = merr.Success() return merr.Success(), nil
return status, nil
} }
// ListCredUsers list all usernames // 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 { if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil return merr.Status(err), nil
} }
entity := in.Entity
err := c.meta.CreateRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: entity.Name}) if err := c.broadcastCreateRole(ctx, in); err != nil {
if err != nil { ctxLog.Warn("fail to create role", zap.Error(err))
errMsg := "fail to create role" metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.FailLabel).Inc()
ctxLog.Warn(errMsg, zap.Error(err)) 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 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.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfRoles.Inc() metrics.RootCoordNumOfRoles.Inc()
return merr.Success(), nil 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 { if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.Status(err), nil return merr.Status(err), nil
} }
for util.IsBuiltinRole(in.GetRoleName()) {
err := merr.WrapErrPrivilegeNotPermitted("the role[%s] is a builtin role, which can't be dropped", in.GetRoleName())
return merr.Status(err), nil
}
if _, err := c.meta.SelectRole(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 { if err := c.broadcastDropRole(ctx, in); err != nil {
grantEntities, err := c.meta.SelectGrant(ctx, util.DefaultTenant, &milvuspb.GrantEntity{ ctxLog.Warn("fail to drop role", zap.Error(err))
Role: &milvuspb.RoleEntity{Name: in.RoleName}, if errors.Is(err, errRoleNotExists) {
DbName: "*", return merr.StatusWithErrorCode(errors.New("not found the role, maybe the role isn't existed or internal system error"), commonpb.ErrorCode_DropRoleFailure), nil
})
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
} }
} return merr.StatusWithErrorCode(err, 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
} }
ctxLog.Debug(method+" success", zap.String("role_name", in.RoleName)) 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 return merr.Status(err), nil
} }
if _, err := c.meta.SelectRole(ctx, util.DefaultTenant, &milvuspb.RoleEntity{Name: in.RoleName}, false); err != nil { if err := c.broadcastOperateUserRole(ctx, in); err != nil {
errMsg := "not found the role, maybe the role isn't existed or internal system error" ctxLog.Warn("fail to operate the user and role", zap.Error(err))
ctxLog.Warn(errMsg, zap.Error(err)) if errors.Is(err, errRoleNotExists) {
return merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_OperateUserRoleFailure), nil return merr.StatusWithErrorCode(errors.New("not found the role, maybe the role isn't existed or internal system error"), 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
} }
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperateUserRoleFailure), nil
} }
err := executeOperateUserRoleTaskSteps(ctx, c, in) ctxLog.Info(method + " success")
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")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc() metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
return merr.Success(), nil return merr.Success(), nil
@ -2558,14 +2495,14 @@ func (c *Core) SelectUser(ctx context.Context, in *milvuspb.SelectUserRequest) (
}, nil }, nil
} }
func (c *Core) isValidRole(entity *milvuspb.RoleEntity) error { func (c *Core) isValidRole(ctx context.Context, entity *milvuspb.RoleEntity) error {
if entity == nil { if entity == nil {
return errors.New("the role entity is nil") return errors.New("the role entity is nil")
} }
if entity.Name == "" { if entity.Name == "" {
return errors.New("the name in the role entity is empty") 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)) 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") 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 := log.Ctx(ctx).With(zap.String("role", typeutil.RootCoordRole), zap.Any("in", in))
ctxLog.Debug(method) ctxLog.Debug(method)
if err := c.operatePrivilegeCommonCheck(ctx, in); err != nil { if err := merr.CheckHealthy(c.GetStateCode()); err != nil {
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil return merr.Status(err), nil
} }
privName := in.Entity.Grantor.Privilege.Name if err := c.broadcastOperatePrivilege(ctx, in); err != nil {
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 {
errMsg := "fail to execute task when operating the privilege" errMsg := "fail to execute task when operating the privilege"
ctxLog.Warn(errMsg, zap.Error(err)) ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil 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 { 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 { 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) 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) 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 { if err := c.isValidObject(in.Entity.Object); err != nil {
return errors.New("the object entity in the request is nil or invalid") 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 return err
} }
entity := in.Entity.Grantor 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), Status: merr.StatusWithErrorCode(errors.New(errMsg), commonpb.ErrorCode_SelectGrantFailure),
}, nil }, 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)) ctxLog.Warn("", zap.Error(err))
return &milvuspb.SelectGrantResponse{ return &milvuspb.SelectGrantResponse{
Status: merr.StatusWithErrorCode(err, commonpb.ErrorCode_SelectGrantFailure), 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 return merr.Status(err), nil
} }
err := executeRestoreRBACTaskSteps(ctx, c, in) if err := c.broadcastRestoreRBACV2(ctx, in); err != nil {
if 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" errMsg := "fail to execute task when restore rbac meta data"
ctxLog.Warn(errMsg, zap.Error(err)) ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeFailure), nil 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 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)) ctxLog.Warn("fail to create privilege group", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreatePrivilegeGroupFailure), nil return merr.StatusWithErrorCode(err, commonpb.ErrorCode_CreatePrivilegeGroupFailure), nil
} }
ctxLog.Debug(method + " success") ctxLog.Info(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc() metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Inc() 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 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)) ctxLog.Warn("fail to drop privilege group", zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DropPrivilegeGroupFailure), nil return merr.StatusWithErrorCode(err, commonpb.ErrorCode_DropPrivilegeGroupFailure), nil
} }
ctxLog.Debug(method + " success") ctxLog.Info(method + " success")
metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc() metrics.RootCoordDDLReqCounter.WithLabelValues(method, metrics.SuccessLabel).Inc()
metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds())) metrics.RootCoordDDLReqLatency.WithLabelValues(method).Observe(float64(tr.ElapseSpan().Milliseconds()))
metrics.RootCoordNumOfPrivilegeGroups.Desc() metrics.RootCoordNumOfPrivilegeGroups.Desc()
@ -3171,8 +3087,7 @@ func (c *Core) OperatePrivilegeGroup(ctx context.Context, in *milvuspb.OperatePr
return merr.Status(err), nil return merr.Status(err), nil
} }
err := executeOperatePrivilegeGroupTaskSteps(ctx, c, in) if err := c.broadcastOperatePrivilegeGroup(ctx, in); err != nil {
if err != nil {
errMsg := "fail to execute task when operate privilege group" errMsg := "fail to execute task when operate privilege group"
ctxLog.Warn(errMsg, zap.Error(err)) ctxLog.Warn(errMsg, zap.Error(err))
return merr.StatusWithErrorCode(err, commonpb.ErrorCode_OperatePrivilegeGroupFailure), nil 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/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb" "github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/internal/distributed/streaming"
"github.com/milvus-io/milvus/internal/metastore/model" "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" 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/internal/util/sessionutil"
"github.com/milvus-io/milvus/pkg/v2/common" "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/etcdpb"
"github.com/milvus-io/milvus/pkg/v2/proto/internalpb" "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/proxypb"
"github.com/milvus-io/milvus/pkg/v2/proto/rootcoordpb" "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"
"github.com/milvus-io/milvus/pkg/v2/util/funcutil" "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/merr"
@ -51,15 +58,39 @@ import (
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
paramtable.Init() paramtable.Init()
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
parameters := []string{"tikv", "etcd"} code := m.Run()
var code int
for _, v := range parameters {
paramtable.Get().Save(paramtable.Get().MetaStoreCfg.MetaStoreType.Key, v)
code = m.Run()
}
os.Exit(code) 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) { func TestRootCoord_CreateDatabase(t *testing.T) {
t.Run("not healthy", func(t *testing.T) { t.Run("not healthy", func(t *testing.T) {
c := newTestCore(withAbnormalCode()) 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) { func TestCore_Stop(t *testing.T) {
t.Run("abnormal stop before component is ready", func(t *testing.T) { t.Run("abnormal stop before component is ready", func(t *testing.T) {
c := &Core{} c := &Core{}
@ -2171,25 +1863,6 @@ func TestCore_BackupRBAC(t *testing.T) {
assert.False(t, merr.Ok(resp.GetStatus())) 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) { func TestCore_getMetastorePrivilegeName(t *testing.T) {
meta := mockrootcoord.NewIMetaTable(t) meta := mockrootcoord.NewIMetaTable(t)
c := newTestCore(withHealthyCode(), withMeta(meta)) c := newTestCore(withHealthyCode(), withMeta(meta))

View File

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

View File

@ -2470,14 +2470,16 @@ type CredentialInfo struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields 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) // encrypted by bcrypt (for higher security level)
EncryptedPassword string `protobuf:"bytes,2,opt,name=encrypted_password,json=encryptedPassword,proto3" json:"encrypted_password,omitempty"` 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"` // Deprecated: Marked as deprecated in internal.proto.
IsSuper bool `protobuf:"varint,4,opt,name=is_super,json=isSuper,proto3" json:"is_super,omitempty"` 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) // 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"` 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 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() { func (x *CredentialInfo) Reset() {
@ -2526,6 +2528,7 @@ func (x *CredentialInfo) GetEncryptedPassword() string {
return "" return ""
} }
// Deprecated: Marked as deprecated in internal.proto.
func (x *CredentialInfo) GetTenant() string { func (x *CredentialInfo) GetTenant() string {
if x != nil { if x != nil {
return x.Tenant return x.Tenant
@ -2533,6 +2536,7 @@ func (x *CredentialInfo) GetTenant() string {
return "" return ""
} }
// Deprecated: Marked as deprecated in internal.proto.
func (x *CredentialInfo) GetIsSuper() bool { func (x *CredentialInfo) GetIsSuper() bool {
if x != nil { if x != nil {
return x.IsSuper 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x74,
0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x18, 0x04, 0x20, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x65,
0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x53, 0x75, 0x70, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x69, 0x73, 0x53,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x75, 0x70, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x5f, 0x70,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x50, 0x61, 0x73, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73,
0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x68, 0x61, 0x32, 0x35, 0x36, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x0a,
0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x69, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x69, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04,
0x63, 0x6b, 0x22, 0x45, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x69, 0x63, 0x6b, 0x22, 0x45, 0x0a, 0x11, 0x4c, 0x69,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
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,
0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 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, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61, 0x73,
0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0x61, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
0x04, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75,
0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53,
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0xee, 0x01, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x0c, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x73, 0x18, 0x02, 0x20,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x73,
0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x6f, 0x6c, 0x65, 0x73, 0x18, 0x03,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x73, 0x12,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x52, 0x0a, 0x10, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x5f, 0x67, 0x72, 0x6f,
0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x75, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6d, 0x69, 0x6c, 0x76,
0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e,
0x65, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e,
0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x66, 0x6f, 0x52, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x47, 0x72, 0x6f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x75, 0x70, 0x73, 0x22, 0x67, 0x0a, 0x19, 0x53, 0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x69, 0x6c, 0x65, 0x52, 0x05, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c,
0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f,
0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x07, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x62, 0x61,
0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x5b, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x02, 0x20,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x22, 0x9a, 0x01, 0x0a,
0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x1a, 0x53, 0x68, 0x6f, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x73,
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, 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, 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, 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, 0x12, 0x47, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65,
0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x73, 0x65, 0x67, 0x6d, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66,
0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x51, 0x69, 0x67, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x45, 0x0a, 0x04, 0x52, 0x61, 0x74,
0x75, 0x6f, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x65, 0x12, 0x2f, 0x0a, 0x02, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e,
0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x61, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74,
0x32, 0x1c, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x02,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x73, 0x67, 0x42, 0x61, 0x73, 0x65, 0x52, 0x04, 0x72, 0x74, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x01, 0x72,
0x62, 0x61, 0x73, 0x65, 0x22, 0x71, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x22, 0x32, 0x0a, 0x0a, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0e,
0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14,
0x33, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70,
0x1b, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x61, 0x74, 0x68, 0x73, 0x22, 0xb7, 0x03, 0x0a, 0x15, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x16,
0x61, 0x74, 0x75, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x0a, 0x04, 0x64, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x02, 0x18, 0x01,
0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x65, 0x74, 0x72, 0x52, 0x04, 0x64, 0x62, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
0x69, 0x63, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x2a, 0x45, 0x0a, 0x09, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x63, 0x6f,
0x63, 0x6f, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x10, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f,
0x00, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x10, 0x01, 0x12, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
0x0e, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x02, 0x12, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e,
0x0d, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x03, 0x2a, 0xc4, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e,
0x01, 0x0a, 0x08, 0x52, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x44, 0x49, 0x44, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0c, 0x70, 0x61, 0x72, 0x74, 0x69,
0x44, 0x4c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x00, 0x12, 0x10, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x6e,
0x0a, 0x0c, 0x44, 0x44, 0x4c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x01, 0x65, 0x6c, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
0x12, 0x0c, 0x0a, 0x08, 0x44, 0x44, 0x4c, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x10, 0x02, 0x12, 0x0c, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x06,
0x0a, 0x08, 0x44, 0x44, 0x4c, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6d,
0x44, 0x44, 0x4c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x12, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, 0x63, 0x68, 0x65,
0x0d, 0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x10, 0x05, 0x12, 0x0d, 0x6d, 0x61, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x63, 0x68,
0x0a, 0x09, 0x44, 0x4d, 0x4c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x10, 0x06, 0x12, 0x0f, 0x0a, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x37, 0x0a, 0x05, 0x66,
0x0b, 0x44, 0x4d, 0x4c, 0x42, 0x75, 0x6c, 0x6b, 0x4c, 0x6f, 0x61, 0x64, 0x10, 0x07, 0x12, 0x0d, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c,
0x0a, 0x09, 0x44, 0x51, 0x4c, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x10, 0x08, 0x12, 0x0c, 0x0a, 0x76, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x08, 0x44, 0x51, 0x4c, 0x51, 0x75, 0x65, 0x72, 0x79, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x61, 0x6c, 0x2e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x66,
0x4d, 0x4c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x10, 0x0a, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x44, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x4c, 0x44, 0x42, 0x10, 0x0b, 0x2a, 0x83, 0x01, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2e, 0x70,
0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4b, 0x65, 0x79, 0x56,
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x61, 0x69, 0x72, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x10, 0x0a, 0x0c, 0x50, 0x72, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x02, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x54,
0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49,
0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0xee,
0x6e, 0x64, 0x65, 0x78, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x06, 0x12, 0x0b, 0x01, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x0a, 0x07, 0x53, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x10, 0x07, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x62, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x09, 0x52, 0x06, 0x64, 0x62, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x6f, 0x6c,
0x2d, 0x69, 0x6f, 0x2f, 0x6d, 0x69, 0x6c, 0x76, 0x75, 0x73, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x76, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61,
0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 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 ( var (