~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
73 lines · 1.8 KB Raw History
package guestidentity

import (
	"fmt"
	"slices"
	"strings"
)

type LoginStep string

const (
	LoginBind LoginStep = "bind"

	LoginChallenge LoginStep = "challenge"

	LoginLogin LoginStep = "login"
)

const maximumLoginField = 4096

var loginFields = map[LoginStep][]string{
	LoginBind:      {"role", "bind_secret", "host_key", "signature"},
	LoginChallenge: {"role"},
	LoginLogin:     {"role", "challenge", "signature"},
}

func ParseLoginStep(step string) (LoginStep, error) {
	s := LoginStep(step)
	if _, ok := loginFields[s]; !ok {
		return "", fmt.Errorf("guestidentity: %q is not a guest login step", step)
	}
	return s, nil
}

func (s LoginStep) Fields() []string {
	fields, ok := loginFields[s]
	if !ok {
		return nil
	}
	return append([]string(nil), fields...)
}

func LoginBody(step LoginStep, input map[string]any) (map[string]string, error) {
	fields, ok := loginFields[step]
	if !ok {
		return nil, fmt.Errorf("guestidentity: %q is not a guest login step", step)
	}
	out := make(map[string]string, len(fields))
	for name, value := range input {
		if !contains(fields, name) {
			return nil, fmt.Errorf("guestidentity: the %s step takes %s, not %s", step, strings.Join(fields, ", "), name)
		}
		text, ok := value.(string)
		if !ok || text == "" || len(text) > maximumLoginField {
			return nil, fmt.Errorf("guestidentity: %s must be a string of 1 to %d bytes", name, maximumLoginField)
		}
		out[name] = text
	}
	for _, name := range fields {
		if out[name] == "" {
			return nil, fmt.Errorf("guestidentity: the %s step needs %s", step, name)
		}
	}
	role := out["role"]
	if !ValidComputeVM(role) {
		return nil, fmt.Errorf("guestidentity: role %q is not a ComputeVM name", role)
	}
	return out, nil
}

func contains(list []string, value string) bool {
	return slices.Contains(list, value)
}