diff --git a/client/client.go b/client/client.go index 9ca981b0..dfb19064 100644 --- a/client/client.go +++ b/client/client.go @@ -192,6 +192,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. diff --git a/client/rbac.go b/client/rbac.go index d758edb3..2e96bede 100644 --- a/client/rbac.go +++ b/client/rbac.go @@ -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 { diff --git a/client/rbac_test.go b/client/rbac_test.go index 038b589e..51f230d1 100644 --- a/client/rbac_test.go +++ b/client/rbac_test.go @@ -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() diff --git a/entity/rbac.go b/entity/rbac.go index cd0f5b9e..ef4e97d9 100644 --- a/entity/rbac.go +++ b/entity/rbac.go @@ -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 { @@ -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