fix: Fix privilege group hasn't been register for validate (#35937)

issue: #35471

---------

Signed-off-by: Wei Liu <wei.liu@zilliz.com>
pull/36004/head
wei liu 2024-09-05 15:35:04 +08:00 committed by GitHub
parent 7b21032d19
commit 32e55a02ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 370 additions and 19 deletions

View File

@ -41,7 +41,7 @@ p = sub, obj, act
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && globMatch(r.obj, p.obj) && globMatch(r.act, p.act) || r.sub == "admin" || (r.sub == p.sub && dbMatch(r.obj, p.obj) && privilegeGroupContains(r.act, p.act))
m = r.sub == p.sub && globMatch(r.obj, p.obj) && globMatch(r.act, p.act) || r.sub == "admin" || (r.sub == p.sub && dbMatch(r.obj, p.obj) && privilegeGroupContains(r.act, p.act, r.obj, p.obj))
`
)
@ -248,20 +248,38 @@ func DBMatchFunc(args ...interface{}) (interface{}, error) {
return db1 == db2, nil
}
func collMatch(requestObj, policyObj string) bool {
_, coll1 := funcutil.SplitObjectName(requestObj[strings.Index(requestObj, "-")+1:])
_, coll2 := funcutil.SplitObjectName(policyObj[strings.Index(policyObj, "-")+1:])
return coll2 == util.AnyWord || coll1 == coll2
}
func PrivilegeGroupContains(args ...interface{}) (interface{}, error) {
requestPrivilege := args[0].(string)
policyPrivilege := args[1].(string)
requestObj := args[2].(string)
policyObj := args[3].(string)
switch policyPrivilege {
case commonpb.ObjectPrivilege_PrivilegeAll.String():
return true, nil
case commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String():
// read only belong to collection object
if !collMatch(requestObj, policyObj) {
return false, nil
}
_, ok := roPrivileges[requestPrivilege]
return ok, nil
case commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String():
// read write belong to collection object
if !collMatch(requestObj, policyObj) {
return false, nil
}
_, ok := rwPrivileges[requestPrivilege]
return ok, nil
case commonpb.ObjectPrivilege_PrivilegeGroupAdmin.String():
// admin belong to global object
_, ok := adminPrivileges[requestPrivilege]
return ok, nil
default:

View File

@ -235,8 +235,9 @@ func TestResourceGroupPrivilege(t *testing.T) {
func TestPrivilegeGroup(t *testing.T) {
ctx := context.Background()
t.Run("Read Only", func(t *testing.T) {
t.Run("grant ReadOnly to single collection", func(t *testing.T) {
paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true")
initPrivilegeGroups()
var err error
ctx = GetContext(context.Background(), "fooo:123456")
@ -248,7 +249,7 @@ func TestPrivilegeGroup(t *testing.T) {
return &internalpb.ListPolicyResponse{
Status: merr.Success(),
PolicyInfos: []string{
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Global.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"),
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "coll1", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"),
},
UserRoles: []string{
funcutil.EncodeUserRoleCache("fooo", "role1"),
@ -258,18 +259,169 @@ func TestPrivilegeGroup(t *testing.T) {
InitMetaCache(ctx, client, queryCoord, mgr)
defer CleanPrivilegeCache()
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll1",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.ShowCollectionsRequest{})
assert.NoError(t, err)
})
t.Run("grant ReadOnly to all collection", func(t *testing.T) {
paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true")
initPrivilegeGroups()
var err error
ctx = GetContext(context.Background(), "fooo:123456")
client := &MockRootCoordClientInterface{}
queryCoord := &mocks.MockQueryCoordClient{}
mgr := newShardClientMgr()
client.listPolicy = func(ctx context.Context, in *internalpb.ListPolicyRequest) (*internalpb.ListPolicyResponse, error) {
return &internalpb.ListPolicyResponse{
Status: merr.Success(),
PolicyInfos: []string{
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String(), "default"),
},
UserRoles: []string{
funcutil.EncodeUserRoleCache("fooo", "role1"),
},
}, nil
}
InitMetaCache(ctx, client, queryCoord, mgr)
defer CleanPrivilegeCache()
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll1",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.ShowCollectionsRequest{})
assert.NoError(t, err)
})
t.Run("grant ReadWrite to single collection", func(t *testing.T) {
paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true")
initPrivilegeGroups()
var err error
ctx = GetContext(context.Background(), "fooo:123456")
client := &MockRootCoordClientInterface{}
queryCoord := &mocks.MockQueryCoordClient{}
mgr := newShardClientMgr()
client.listPolicy = func(ctx context.Context, in *internalpb.ListPolicyRequest) (*internalpb.ListPolicyResponse, error) {
return &internalpb.ListPolicyResponse{
Status: merr.Success(),
PolicyInfos: []string{
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "coll1", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"),
},
UserRoles: []string{
funcutil.EncodeUserRoleCache("fooo", "role1"),
},
}, nil
}
InitMetaCache(ctx, client, queryCoord, mgr)
defer CleanPrivilegeCache()
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{
CollectionName: "coll2",
})
assert.Error(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.CreateResourceGroupRequest{})
assert.Error(t, err)
})
t.Run("Read Write", func(t *testing.T) {
t.Run("grant ReadWrite to all collection", func(t *testing.T) {
paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true")
initPrivilegeGroups()
var err error
ctx = GetContext(context.Background(), "fooo:123456")
@ -281,7 +433,7 @@ func TestPrivilegeGroup(t *testing.T) {
return &internalpb.ListPolicyResponse{
Status: merr.Success(),
PolicyInfos: []string{
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Global.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"),
funcutil.PolicyForPrivilege("role1", commonpb.ObjectType_Collection.String(), "*", commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String(), "default"),
},
UserRoles: []string{
funcutil.EncodeUserRoleCache("fooo", "role1"),
@ -291,16 +443,44 @@ func TestPrivilegeGroup(t *testing.T) {
InitMetaCache(ctx, client, queryCoord, mgr)
defer CleanPrivilegeCache()
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{})
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{
CollectionName: "coll1",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.QueryRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.SearchRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.InsertRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.DeleteRequest{
CollectionName: "coll2",
})
assert.NoError(t, err)
_, err = PrivilegeInterceptor(GetContext(context.Background(), "fooo:123456"), &milvuspb.CreateResourceGroupRequest{})
@ -309,6 +489,7 @@ func TestPrivilegeGroup(t *testing.T) {
t.Run("Admin", func(t *testing.T) {
paramtable.Get().Save(Params.CommonCfg.AuthorizationEnabled.Key, "true")
initPrivilegeGroups()
var err error
ctx = GetContext(context.Background(), "fooo:123456")

View File

@ -2524,7 +2524,7 @@ func (c *Core) isValidGrantor(entity *milvuspb.GrantorEntity, object string) err
return nil
}
}
return fmt.Errorf("not found the privilege name[%s]", entity.Privilege.Name)
return fmt.Errorf("not found the privilege name[%s] in object[%s]", entity.Privilege.Name, object)
}
// OperatePrivilege operate the privilege, including grant and revoke

View File

@ -57,8 +57,9 @@ const (
NonDBID = int64(0)
InvalidDBID = int64(-1)
PrivilegeWord = "Privilege"
AnyWord = "*"
PrivilegeWord = "Privilege"
PrivilegeGroupWord = "PrivilegeGroup"
AnyWord = "*"
IdentifierKey = "identifier"
@ -110,6 +111,8 @@ var (
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeShowPartitions.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeHasPartition.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGetFlushState.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadOnly.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupReadWrite.String()),
},
commonpb.ObjectType_Global.String(): {
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeAll.String()),
@ -146,6 +149,7 @@ var (
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDropAlias.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeDescribeAlias.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeListAliases.String()),
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeGroupAdmin.String()),
},
commonpb.ObjectType_User.String(): {
MetaStore2API(commonpb.ObjectPrivilege_PrivilegeUpdateUser.String()),
@ -198,8 +202,6 @@ var (
commonpb.ObjectPrivilege_PrivilegeGetStatistics.String(),
commonpb.ObjectPrivilege_PrivilegeCreateIndex.String(),
commonpb.ObjectPrivilege_PrivilegeDropIndex.String(),
commonpb.ObjectPrivilege_PrivilegeCreateCollection.String(),
commonpb.ObjectPrivilege_PrivilegeDropCollection.String(),
commonpb.ObjectPrivilege_PrivilegeCreatePartition.String(),
commonpb.ObjectPrivilege_PrivilegeDropPartition.String(),
commonpb.ObjectPrivilege_PrivilegeLoad.String(),
@ -216,6 +218,8 @@ var (
commonpb.ObjectPrivilege_PrivilegeDropAlias.String(),
}
AdminPrivilegeGroup = []string{
commonpb.ObjectPrivilege_PrivilegeCreateCollection.String(),
commonpb.ObjectPrivilege_PrivilegeDropCollection.String(),
commonpb.ObjectPrivilege_PrivilegeQuery.String(),
commonpb.ObjectPrivilege_PrivilegeSearch.String(),
commonpb.ObjectPrivilege_PrivilegeIndexDetail.String(),
@ -291,7 +295,11 @@ func StringList(stringMap map[string]struct{}) []string {
// MetaStore2API convert meta-store's privilege name to api's
// example: PrivilegeAll -> All
func MetaStore2API(name string) string {
return name[strings.Index(name, PrivilegeWord)+len(PrivilegeWord):]
prefix := PrivilegeWord
if strings.Contains(name, PrivilegeGroupWord) {
prefix = PrivilegeGroupWord
}
return name[strings.Index(name, prefix)+len(prefix):]
}
func PrivilegeNameForAPI(name string) string {
@ -303,10 +311,17 @@ func PrivilegeNameForAPI(name string) string {
}
func PrivilegeNameForMetastore(name string) string {
// check if name is single privilege
dbPrivilege := PrivilegeWord + name
_, ok := commonpb.ObjectPrivilege_value[dbPrivilege]
if !ok {
return ""
// check if name is privilege group
dbPrivilege := PrivilegeGroupWord + name
_, ok := commonpb.ObjectPrivilege_value[dbPrivilege]
if !ok {
return ""
}
return dbPrivilege
}
return dbPrivilege
}

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 rbac
import (
"context"
"testing"
"github.com/stretchr/testify/suite"
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/pkg/util"
"github.com/milvus-io/milvus/pkg/util/merr"
"github.com/milvus-io/milvus/pkg/util/paramtable"
"github.com/milvus-io/milvus/tests/integration"
)
type PrivilegeGroupTestSuite struct {
integration.MiniClusterSuite
}
func (s *PrivilegeGroupTestSuite) SetupSuite() {
paramtable.Init()
paramtable.Get().Save(paramtable.Get().QueryCoordCfg.BalanceCheckInterval.Key, "1000")
paramtable.Get().Save(paramtable.Get().QueryNodeCfg.GracefulStopTimeout.Key, "1")
paramtable.Get().Save(paramtable.Get().CommonCfg.AuthorizationEnabled.Key, "true")
s.Require().NoError(s.SetupEmbedEtcd())
}
func (s *PrivilegeGroupTestSuite) TestPrivilegeGroup() {
ctx := GetContext(context.Background(), "root:123456")
// test empty rbac content
resp, err := s.Cluster.Proxy.BackupRBAC(ctx, &milvuspb.BackupRBACMetaRequest{})
s.NoError(err)
s.True(merr.Ok(resp.GetStatus()))
s.Equal("", resp.GetRBACMeta().String())
// generate some rbac content
roleName := "test_role"
resp1, err := s.Cluster.Proxy.CreateRole(ctx, &milvuspb.CreateRoleRequest{
Entity: &milvuspb.RoleEntity{
Name: roleName,
},
})
s.NoError(err)
s.True(merr.Ok(resp1))
resp2, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "ReadOnly"},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp2))
resp3, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "ReadWrite"},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp3))
resp4, err := s.Cluster.Proxy.OperatePrivilege(ctx, &milvuspb.OperatePrivilegeRequest{
Type: milvuspb.OperatePrivilegeType_Grant,
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{Name: util.UserRoot},
Privilege: &milvuspb.PrivilegeEntity{Name: "Admin"},
},
},
})
s.NoError(err)
s.True(merr.Ok(resp4))
resp5, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Global.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
},
})
s.NoError(err)
s.True(merr.Ok(resp5.GetStatus()))
s.Len(resp5.GetEntities(), 1)
resp6, err := s.Cluster.Proxy.SelectGrant(ctx, &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{Name: roleName},
Object: &milvuspb.ObjectEntity{Name: commonpb.ObjectType_Collection.String()},
ObjectName: util.AnyWord,
DbName: util.AnyWord,
},
})
s.NoError(err)
s.True(merr.Ok(resp6.GetStatus()))
s.Len(resp6.GetEntities(), 2)
}
func TestPrivilegeGroup(t *testing.T) {
suite.Run(t, new(PrivilegeGroupTestSuite))
}