Skip to content
Snippets Groups Projects
user.go 6.54 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabian Seidl's avatar
    Fabian Seidl committed
    	"encoding/base64"
    
    	cpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/conflict"
    
    	apb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/rbac"
    
    	"code.fbi.h-da.de/danet/gosdn/controller/conflict"
    
    	"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"
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    	"github.com/sethvargo/go-password/password"
    
    	log "github.com/sirupsen/logrus"
    	"google.golang.org/grpc/codes"
    	"google.golang.org/grpc/status"
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    
    	"golang.org/x/crypto/argon2"
    
    
    	rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac"
    
    // UserServer holds a JWTManager and represents a UserServiceServer.
    type UserServer struct {
    
    	apb.UnimplementedUserServiceServer
    
    	jwtManager  *rbac.JWTManager
    	userService rbacInterfaces.UserService
    
    // NewUserServer receives a JWTManager and a UserService and returns a new UserServer.
    
    func NewUserServer(jwtManager *rbac.JWTManager, userService rbacInterfaces.UserService) *UserServer {
    	return &UserServer{
    
    		jwtManager:  jwtManager,
    		userService: userService,
    
    // CreateUsers creates new users, can be 1 or more.
    
    func (u UserServer) 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)
    
    
    	for _, user := range request.User {
    
    		roles := map[string]string{}
    
    		for key, elem := range user.Roles {
    
    			_, err := uuid.Parse(key)
    			if err != nil {
    				return nil, handleRPCError(labels, err)
    			}
    			roles[key] = elem
    		}
    
    
    Fabian Seidl's avatar
    Fabian Seidl committed
    		// Generate a salt that is 16 characters long with 3 digits, 0 symbols,
    		// allowing upper and lower case letters, disallowing repeat characters.
    		salt, err := password.Generate(16, 3, 0, true, false)
    		if err != nil {
    			log.Error(err)
    			return nil, status.Errorf(codes.Aborted, "%v", err)
    		}
    
    
    		hashedPassword := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(user.Password), []byte(salt), 1, 64*1024, 4, 32))
    
    		user := rbac.NewUser(uuid.New(), user.Name, roles, string(hashedPassword), user.Token, salt, conflict.Metadata{ResourceVersion: 0})
    
    		err = u.userService.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 UserServer) 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)
    
    
    	userID, err := uuid.Parse(request.Id)
    	if err != nil {
    		return nil, fmt.Errorf("could not parse user uuid")
    	}
    
    	userData, err := u.userService.Get(store.Query{Name: request.Name, ID: userID})
    
    	if err != nil {
    		return nil, err
    	}
    
    	user := &apb.User{
    		Id:    userData.ID().String(),
    		Name:  userData.Name(),
    		Roles: userData.GetRoles(),
    
    		Metadata: &cpb.Metadata{
    			ResourceVersion: int64(userData.GetMetadata().ResourceVersion),
    		},
    
    	}
    
    	return &apb.GetUserResponse{
    		Timestamp: time.Now().UnixNano(),
    		Status:    apb.Status_STATUS_OK,
    		User:      user,
    	}, nil
    }
    
    
    // GetUsers returns all availbale users.
    
    func (u UserServer) 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 := u.userService.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(),
    
    			Metadata: &cpb.Metadata{
    				ResourceVersion: int64(u.GetMetadata().ResourceVersion),
    			},
    
    		})
    	}
    
    	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 UserServer) 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)
    
    
    	for _, user := range request.User {
    		uid, err := uuid.Parse(user.Id)
    
    		if err != nil {
    			return nil, handleRPCError(labels, err)
    		}
    
    
    		storedUser, err := u.userService.Get(store.Query{ID: uid})
    
    		if err != nil {
    			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
    		}
    
    
    		hashedPassword := base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(user.Password), []byte(storedUser.GetSalt()), 1, 64*1024, 4, 32))
    
    		userToUpdate := rbac.NewUser(uid, user.Name, user.Roles, string(hashedPassword), user.Token, storedUser.GetSalt(), conflict.Metadata{
    			ResourceVersion: int(user.Metadata.ResourceVersion)})
    
    		usr, _ := userToUpdate.(*rbac.User)
    		usr.Metadata.ResourceVersion = int(user.Metadata.ResourceVersion)
    
    		err = u.userService.Update(usr)
    
    		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 UserServer) 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 _, user := range request.Username {
    		userToDelete, err := u.userService.Get(store.Query{Name: user})
    
    		if err != nil {
    			return nil, status.Errorf(codes.Canceled, "user not found %v", err)
    		}
    
    
    		err = u.userService.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
    }