Skip to content

Commit

Permalink
enhance: Add ListGrants and ListGrant RBAC methods (#743)
Browse files Browse the repository at this point in the history
Resolves #744

Two methods:

```
// ListGrant lists a grant info for the role and the specific object
ListGrant(ctx context.Context, role string, object string, objectName string, dbName string) ([]entity.RoleGrants, error)
// ListGrants lists all assigned privileges and objects for the role.
ListGrants(ctx context.Context, role string, dbName string) ([]entity.RoleGrants, error)
```

And a structure:

```
type RoleGrants struct {
	Object        string
	ObjectName    string
	RoleName      string
	GrantorName   string
	PrivilegeName string
	DbName        string
}
```

PyMilvus's implementation (and signatures, names) of such methods was
taken as an inspiration here.

Thanks!

Signed-off-by: punkerpunker <[email protected]>
Co-authored-by: punkerpunker <[email protected]>
  • Loading branch information
punkerpunker and punkerpunker authored May 30, 2024
1 parent 8309bdf commit be3ac4b
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 1 deletion.
4 changes: 4 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ type Client interface {
DescribeUser(ctx context.Context, username string) (entity.UserDescription, error)
// DescribeUsers describe all users attributes in the system
DescribeUsers(ctx context.Context) ([]entity.UserDescription, error)
// ListGrant lists a grant info for the role and the specific object
ListGrant(ctx context.Context, role string, object string, objectName string, dbName string) ([]entity.RoleGrants, error)
// ListGrants lists all assigned privileges and objects for the role.
ListGrants(ctx context.Context, role string, dbName string) ([]entity.RoleGrants, error)
// Grant adds privilege for role.
Grant(ctx context.Context, role string, objectType entity.PriviledgeObjectType, object string) error
// Revoke removes privilege from role.
Expand Down
96 changes: 96 additions & 0 deletions client/rbac.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,102 @@ func (c *GrpcClient) DescribeUsers(ctx context.Context) ([]entity.UserDescriptio
return userDescriptions, nil
}

// ListGrants lists the role grants in the system
func (c *GrpcClient) ListGrants(ctx context.Context, role string, dbName string) ([]entity.RoleGrants, error) {
RoleGrantsList := make([]entity.RoleGrants, 0)
if c.Service == nil {
return RoleGrantsList, ErrClientNotReady
}

req := &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{
Name: role,
},
DbName: dbName,
},
}

resp, err := c.Service.SelectGrant(ctx, req)

if err != nil {
return RoleGrantsList, err
}
if err = handleRespStatus(resp.GetStatus()); err != nil {
return RoleGrantsList, err
}

results := resp.GetEntities()

if len(results) == 0 {
return RoleGrantsList, nil
}

for _, roleEntity := range results {
RoleGrant := entity.RoleGrants{
Object: roleEntity.Object.Name,
ObjectName: roleEntity.ObjectName,
RoleName: roleEntity.Role.Name,
GrantorName: roleEntity.Grantor.User.Name,
PrivilegeName: roleEntity.Grantor.Privilege.Name,
DbName: roleEntity.DbName,
}
RoleGrantsList = append(RoleGrantsList, RoleGrant)
}

return RoleGrantsList, nil
}

// ListGrant lists a grant info for the role and the specific object
func (c *GrpcClient) ListGrant(ctx context.Context, role string, object string, objectName string, dbName string) ([]entity.RoleGrants, error) {
RoleGrantsList := make([]entity.RoleGrants, 0)
if c.Service == nil {
return RoleGrantsList, ErrClientNotReady
}

req := &milvuspb.SelectGrantRequest{
Entity: &milvuspb.GrantEntity{
Role: &milvuspb.RoleEntity{
Name: role,
},
Object: &milvuspb.ObjectEntity{
Name: object,
},
ObjectName: objectName,
DbName: dbName,
},
}

resp, err := c.Service.SelectGrant(ctx, req)

if err != nil {
return RoleGrantsList, err
}
if err = handleRespStatus(resp.GetStatus()); err != nil {
return RoleGrantsList, err
}

results := resp.GetEntities()

if len(results) == 0 {
return RoleGrantsList, nil
}

for _, roleEntity := range results {
RoleGrant := entity.RoleGrants{
Object: roleEntity.Object.Name,
ObjectName: roleEntity.ObjectName,
RoleName: roleEntity.Role.Name,
GrantorName: roleEntity.Grantor.User.Name,
PrivilegeName: roleEntity.Grantor.Privilege.Name,
DbName: roleEntity.DbName,
}
RoleGrantsList = append(RoleGrantsList, RoleGrant)
}

return RoleGrantsList, nil
}

// Grant adds object privileged for role.
func (c *GrpcClient) Grant(ctx context.Context, role string, objectType entity.PriviledgeObjectType, object string) error {
if c.Service == nil {
Expand Down
118 changes: 118 additions & 0 deletions client/rbac_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,124 @@ func (s *RBACSuite) TestDescribeUsers() {
})
}

func (s *RBACSuite) TestListGrant() {
ctx := context.Background()
roleName := "testRole"
object := "testObject"
objectName := "testObjectName"
dbName := "testDB"

s.Run("normal run", func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer s.resetMock()

mockResults := []*milvuspb.GrantEntity{
{
Object: &milvuspb.ObjectEntity{
Name: object,
},
ObjectName: objectName,
Role: &milvuspb.RoleEntity{
Name: roleName,
},
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{
Name: "grantorUser",
},
Privilege: &milvuspb.PrivilegeEntity{
Name: "testPrivilege",
},
},
DbName: dbName,
},
}

s.mock.EXPECT().SelectGrant(mock.Anything, mock.Anything).Run(func(ctx context.Context, req *milvuspb.SelectGrantRequest) {
s.Equal(req.GetEntity().GetRole().GetName(), roleName)
s.Equal(req.GetEntity().GetObject().GetName(), object)
s.Equal(req.GetEntity().GetObjectName(), objectName)
s.Equal(req.GetEntity().GetDbName(), dbName)
}).Return(&milvuspb.SelectGrantResponse{
Status: &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success},
Entities: mockResults,
}, nil)

grants, err := s.client.ListGrant(ctx, roleName, object, objectName, dbName)

s.NoError(err)
s.Len(grants, 1)

expectedGrant := entity.RoleGrants{
Object: object,
ObjectName: objectName,
RoleName: roleName,
GrantorName: "grantorUser",
PrivilegeName: "testPrivilege",
DbName: dbName,
}

s.Equal(expectedGrant, grants[0])
})
}

func (s *RBACSuite) TestListGrants() {
ctx := context.Background()
roleName := "testRole"
dbName := "testDB"

s.Run("normal run", func() {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
defer s.resetMock()

mockResults := []*milvuspb.GrantEntity{
{
Object: &milvuspb.ObjectEntity{
Name: "testObject",
},
ObjectName: "testObjectName",
Role: &milvuspb.RoleEntity{
Name: roleName,
},
Grantor: &milvuspb.GrantorEntity{
User: &milvuspb.UserEntity{
Name: "grantorUser",
},
Privilege: &milvuspb.PrivilegeEntity{
Name: "testPrivilege",
},
},
DbName: dbName,
},
}

s.mock.EXPECT().SelectGrant(mock.Anything, mock.Anything).Run(func(ctx context.Context, req *milvuspb.SelectGrantRequest) {
s.Equal(req.GetEntity().GetRole().GetName(), roleName)
s.Equal(req.GetEntity().GetDbName(), dbName)
}).Return(&milvuspb.SelectGrantResponse{
Status: &commonpb.Status{ErrorCode: commonpb.ErrorCode_Success},
Entities: mockResults,
}, nil)

grants, err := s.client.ListGrants(ctx, roleName, dbName)

s.NoError(err)
s.Len(grants, 1)

expectedGrant := entity.RoleGrants{
Object: "testObject",
ObjectName: "testObjectName",
RoleName: roleName,
GrantorName: "grantorUser",
PrivilegeName: "testPrivilege",
DbName: dbName,
}

s.Equal(expectedGrant, grants[0])
})
}

func (s *RBACSuite) TestGrant() {
ctx := context.Background()

Expand Down
14 changes: 13 additions & 1 deletion entity/rbac.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package entity

import common "github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
import (
common "github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
)

// User is the model for RBAC user object.
type User struct {
Expand All @@ -13,6 +15,16 @@ type UserDescription struct {
Roles []string
}

// RoleGrants is the model for RBAC role description object.
type RoleGrants struct {
Object string
ObjectName string
RoleName string
GrantorName string
PrivilegeName string
DbName string
}

// Role is the model for RBAC role object.
type Role struct {
Name string
Expand Down

0 comments on commit be3ac4b

Please sign in to comment.