Skip to content
Snippets Groups Projects
user.go 5.69 KiB
Newer Older
  • Learn to ignore specific revisions
  • package server
    
    import (
    	"context"
    	"fmt"
    	"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"
    )
    
    // User holds a JWTManager and represents a UserServiceServer.
    type User struct {
    	apb.UnimplementedUserServiceServer
    	jwtManager *rbac.JWTManager
    }
    
    // NewUserServer receives a JWTManager and returns a new UserServer.
    func NewUserServer(jwtManager *rbac.JWTManager) *User {
    	return &User{
    		jwtManager: jwtManager,
    	}
    }
    
    // CreateUsers creates new users, can be 1 or more
    func (u User) CreateUsers(ctx context.Context, request *apb.CreateUsersRequest) (*apb.CreateUsersResponse, error) {
    	labels := prometheus.Labels{"service": "auth", "rpc": "post"}
    	start := metrics.StartHook(labels, grpcRequestsTotal)
    	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
    
    	// TODO: implement check if user is allowed to create users with this role
    	// e.g. non-admin shouldn't be allowed to create admin users
    	for _, u := range request.User {
    		roles := map[string]string{}
    		for key, elem := range u.Roles {
    			fmt.Printf("k: %v v: %v\n", key, elem)
    			_, err := uuid.Parse(key)
    			if err != nil {
    				return nil, handleRPCError(labels, err)
    			}
    			roles[key] = elem
    		}
    
    		user := rbac.NewUser(uuid.New(), u.Name, roles, u.Password, u.Token)
    		err := userc.Add(user)
    		if err != nil {
    			log.Error(err)
    			return nil, status.Errorf(codes.Aborted, "%v", err)
    		}
    	}
    
    	return &apb.CreateUsersResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    	}, nil
    }
    
    // GetUser returns one user by name.
    func (u User) GetUser(ctx context.Context, request *apb.GetUserRequest) (*apb.GetUserResponse, error) {
    	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
    	start := metrics.StartHook(labels, grpcRequestsTotal)
    	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
    
    	// TODO: implement check if user is allowed to get this user data; only their own if not admin
    	userData, err := userc.Get(store.Query{Name: request.Name})
    	if err != nil {
    		return nil, err
    	}
    
    	user := &apb.User{
    		Id:    userData.ID().String(),
    		Name:  userData.Name(),
    		Roles: userData.GetRoles(),
    	}
    
    	return &apb.GetUserResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    		User:      user,
    	}, nil
    }
    
    // GetUsers returns all availbale users
    func (u User) GetUsers(ctx context.Context, request *apb.GetUsersRequest) (*apb.GetUsersResponse, error) {
    	labels := prometheus.Labels{"service": "auth", "rpc": "get"}
    	start := metrics.StartHook(labels, grpcRequestsTotal)
    	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
    
    	userList, err := userc.GetAll()
    	if err != nil {
    		return nil, err
    	}
    
    	users := []*apb.User{}
    	for _, u := range userList {
    		users = append(users, &apb.User{
    			Id:    u.ID().String(),
    			Name:  u.Name(),
    			Roles: u.GetRoles(),
    		})
    	}
    
    	return &apb.GetUsersResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    		User:      users,
    	}, nil
    }
    
    // UpdateUsers updates the user data of one or more users provided in the request
    func (u User) UpdateUsers(ctx context.Context, request *apb.UpdateUsersRequest) (*apb.UpdateUsersResponse, 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 user they try to update; only their own if not admin
    	for _, u := range request.User {
    		uid, err := uuid.Parse(u.Id)
    		if err != nil {
    			return nil, handleRPCError(labels, err)
    		}
    
    		_, err = userc.Get(store.Query{ID: uid})
    		if err != nil {
    			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
    		}
    
    		userToUpdate := rbac.NewUser(uid, u.Name, u.Roles, u.Password, u.Token)
    
    		err = userc.Update(userToUpdate)
    		if err != nil {
    			return nil, status.Errorf(codes.Aborted, "could not update user %v", err)
    		}
    	}
    
    	return &apb.UpdateUsersResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    	}, nil
    }
    
    // DeleteUsers deletes one or more users provided in the request
    func (u User) DeleteUsers(ctx context.Context, request *apb.DeleteUsersRequest) (*apb.DeleteUsersResponse, error) {
    	labels := prometheus.Labels{"service": "auth", "rpc": "delete"}
    	start := metrics.StartHook(labels, grpcRequestsTotal)
    	defer metrics.FinishHook(labels, start, grpcRequestDurationSecondsTotal, grpcRequestDurationSeconds)
    
    	for _, u := range request.Username {
    		userToDelete, err := userc.Get(store.Query{Name: u})
    		if err != nil {
    			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
    		}
    
    		err = userc.Delete(userToDelete)
    		if err != nil {
    			return nil, status.Errorf(codes.Aborted, "error deleting user %v", err)
    		}
    	}
    	return &apb.DeleteUsersResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    	}, nil
    }
    
    func (u User) isValidUser(user rbac.User) (bool, error) {
    	storedUser, err := userc.Get(store.Query{Name: user.Name()})
    	if err != nil {
    		return false, err
    	} else if storedUser == nil {
    		return false, status.Errorf(codes.Aborted, "no user object")
    	}
    
    	if storedUser.Name() == user.Name() {
    		if storedUser.GetPassword() == user.GetPassword() {
    			return true, nil
    		}
    	}
    
    	return false, status.Errorf(codes.Unauthenticated, "incorrect user name or password")
    }