Skip to content
Snippets Groups Projects
role.go 7.68 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	"errors"
    
    	"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 {
    
    	return &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)
    
    		if err != nil {
    			return nil, status.Errorf(codes.Aborted, "error deleting role %v", err)
    		}
    	}
    
    	return &apb.DeleteRolesResponse{
    		Timestamp: time.Now().UnixNano(),
    	}, nil
    }