Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
role.go 6.34 KiB
package server

import (
	"context"
	"time"

	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/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/google/uuid"
	"github.com/prometheus/client_golang/prometheus"
	log "github.com/sirupsen/logrus"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// Role holds a JWTManager and represents a RoleServiceServer.
type Role struct {
	apb.UnimplementedRoleServiceServer
	jwtManager *rbac.JWTManager
}

// NewRoleServer receives a JWTManager and returns a new Role.
func NewRoleServer(jwtManager *rbac.JWTManager) *Role {
	return &Role{
		jwtManager: jwtManager,
	}
}

// CreateRoles creates one are multiple new roles.
func (r Role) 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)

	for _, r := range request.Roles {
		role := rbac.NewRole(uuid.New(), r.Name, r.Description, r.Permissions)

		err := rolec.Add(role)
		if err != nil {
			log.Error(err)
			return nil, status.Errorf(codes.Aborted, "%v", err)
		}
	}

	return &apb.CreateRolesResponse{
		Timestamp: time.Now().UnixNano(),
		Status:    apb.Status_STATUS_OK,
	}, nil
}

// GetRole returns one role with its permissions found by name.
func (r Role) 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)

	roleData, err := rolec.Get(store.Query{Name: request.RoleName})
	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(),
		Status:    apb.Status_STATUS_OK,
		Role:      role,
	}, nil
}

// GetRoles returns all roles with their permissions.
func (r Role) 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)

	roleList, err := rolec.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(),
		Status:    apb.Status_STATUS_OK,
		Roles:     roles,
	}, nil
}

// UpdateRoles updates data of the provided roles.
func (r Role) 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)

	// TODO: check if current user is allowed to update the role they try to update; only their own if not admin
	for _, r := range request.Roles {
		rid, err := uuid.Parse(r.Id)
		if err != nil {
			return nil, handleRPCError(labels, err)
		}

		_, err = rolec.Get(store.Query{ID: rid})
		if err != nil {
			return nil, status.Errorf(codes.Canceled, "role not found %v", err)
		}

		roleToUpdate := rbac.NewRole(rid, r.Name, r.Description, r.Permissions)
		err = rolec.Update(roleToUpdate)
		if err != nil {
			return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
		}
	}

	return &apb.UpdateRolesResponse{
		Timestamp: time.Now().UnixNano(),
		Status:    apb.Status_STATUS_OK,
	}, nil
}

// DeletePermissionsForRole deletes the provided permissions from one role found by name.
func (r Role) 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)

	roleToUpdate, err := rolec.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 = rolec.Update(roleToUpdate)
	if err != nil {
		return nil, status.Errorf(codes.Aborted, "could not update role %v", err)
	}

	return &apb.DeletePermissionsForRoleResponse{
		Timestamp: time.Now().UnixNano(),
		Status:    apb.Status_STATUS_OK,
	}, nil
}

// DeleteRoles deletes all the provided roles with their permissions.
func (r Role) 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)

	for _, r := range request.RoleName {
		roleToDelete, err := rolec.Get(store.Query{Name: r})
		if err != nil {
			return nil, status.Errorf(codes.Canceled, "role not found")
		}

		err = rolec.Delete(roleToDelete)
		if err != nil {
			return nil, status.Errorf(codes.Aborted, "error deleting role %v", err)
		}
	}

	return &apb.DeleteRolesResponse{
		Timestamp: time.Now().UnixNano(),
		Status:    apb.Status_STATUS_OK,
	}, nil
}