Newer
Older
package server
import (
"context"
"time"
apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/metrics"
"code.fbi.h-da.de/danet/gosdn/controller/rbac"
"code.fbi.h-da.de/danet/gosdn/controller/store"
"github.com/bufbuild/protovalidate-go"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/reflect/protoreflect"
// RoleServer holds a JWTManager and represents a RoleServiceServer.
type RoleServer struct {
apb.UnimplementedRoleServiceServer
jwtManager *rbac.JWTManager
roleService rbacInterfaces.RoleService
protoValidator *protovalidate.Validator
// NewRoleServer receives a JWTManager and a RoleService and returns a new RoleServer.
func NewRoleServer(
jwtManager *rbac.JWTManager,
roleService rbacInterfaces.RoleService,
protoValidator *protovalidate.Validator,
) *RoleServer {
jwtManager: jwtManager,
roleService: roleService,
protoValidator: protoValidator,
func (r RoleServer) checkForValidationErrors(request protoreflect.ProtoMessage) error {
err := r.protoValidator.Validate(request)
if err != nil {
var valErr *protovalidate.ValidationError
if ok := errors.As(err, &valErr); ok {
protoErr := valErr.ToProto()
grpcError, _ := status.New(codes.Aborted, "Validation failed").WithDetails(protoErr)
return grpcError.Err()
}
return status.Errorf(codes.Aborted, "%v", err)
}
return nil
}
// CreateRoles creates one are multiple new roles.
func (r RoleServer) CreateRoles(ctx context.Context, request *apb.CreateRolesRequest) (*apb.CreateRolesResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
for _, rrole := range request.Roles {
role := rbac.NewRole(uuid.New(), rrole.Name, rrole.Description, rrole.Permissions)
err := r.roleService.Add(role)
if err != nil {
log.Error(err)
return nil, status.Errorf(codes.Aborted, "%v", err)
}
}
return &apb.CreateRolesResponse{
Timestamp: time.Now().UnixNano(),
}, nil
}
// GetRole returns one role with its permissions found by name.
func (r RoleServer) GetRole(ctx context.Context, request *apb.GetRoleRequest) (*apb.GetRoleResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
roleID, err := uuid.Parse(request.Id)
if err != nil {
return nil, fmt.Errorf("could not parse role uuid")
}
roleData, err := r.roleService.Get(store.Query{Name: request.RoleName, ID: roleID})
if err != nil {
return nil, err
}
role := &apb.Role{
Id: roleData.ID().String(),
Name: roleData.Name(),
Description: roleData.GetDescription(),
Permissions: roleData.GetPermissions(),
}
return &apb.GetRoleResponse{
Timestamp: time.Now().UnixNano(),
Role: role,
}, nil
}
// GetRoles returns all roles with their permissions.
func (r RoleServer) GetRoles(ctx context.Context, request *apb.GetRolesRequest) (*apb.GetRolesResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "get"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
roleList, err := r.roleService.GetAll()
if err != nil {
return nil, err
}
roles := []*apb.Role{}
for _, r := range roleList {
roles = append(roles, &apb.Role{
Id: r.ID().String(),
Name: r.Name(),
Description: r.GetDescription(),
Permissions: r.GetPermissions(),
})
}
return &apb.GetRolesResponse{
Timestamp: time.Now().UnixNano(),
Roles: roles,
}, nil
}
// UpdateRoles updates data of the provided roles.
func (r RoleServer) UpdateRoles(ctx context.Context, request *apb.UpdateRolesRequest) (*apb.UpdateRolesResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "post"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
for _, role := range request.Roles {
rid, err := uuid.Parse(role.Id)
if err != nil {
return nil, handleRPCError(labels, err)
}
_, err = r.roleService.Get(store.Query{ID: rid})
if err != nil {
return nil, status.Errorf(codes.Canceled, "role not found %v", err)
}
roleToUpdate := rbac.NewRole(rid, role.Name, role.Description, role.Permissions)
err = r.roleService.Update(roleToUpdate)
if err != nil {
return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
}
}
return &apb.UpdateRolesResponse{
Timestamp: time.Now().UnixNano(),
}, nil
}
// DeletePermissionsForRole deletes the provided permissions from one role found by name.
func (r RoleServer) DeletePermissionsForRole(ctx context.Context, request *apb.DeletePermissionsForRoleRequest) (*apb.DeletePermissionsForRoleResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
roleToUpdate, err := r.roleService.Get(store.Query{Name: request.RoleName})
if err != nil {
return nil, status.Errorf(codes.Canceled, "role not found %v", err)
}
// checks if there is at least one valid permission to delete
// in the provided set of permissions to delete
nonFound := true
for _, perm := range roleToUpdate.GetPermissions() {
for _, permToDelete := range request.PermissionsToDelete {
if perm == permToDelete {
nonFound = false
break
}
}
if !nonFound {
break
}
}
if nonFound {
return nil, status.Errorf(codes.Canceled, "no fitting permissions")
}
// updates the existing role with the trimmed set of permissions
roleToUpdate.RemovePermissionsFromRole(request.PermissionsToDelete)
err = r.roleService.Update(roleToUpdate)
if err != nil {
return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
}
return &apb.DeletePermissionsForRoleResponse{
Timestamp: time.Now().UnixNano(),
}, nil
}
// DeleteRoles deletes all the provided roles with their permissions.
func (r RoleServer) DeleteRoles(ctx context.Context, request *apb.DeleteRolesRequest) (*apb.DeleteRolesResponse, error) {
labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
start := metrics.StartHook(labels, grpcRequestsTotal)
defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
err := r.checkForValidationErrors(request)
if err != nil {
return nil, err
for _, role := range request.RoleName {
roleToDelete, err := r.roleService.Get(store.Query{Name: role})
if err != nil {
return nil, status.Errorf(codes.Canceled, "role not found")
}
err = r.roleService.Delete(roleToDelete)