#!/usr/bin/env bash validate_command() { local PERSON="$1" local cmd="$2" shift 2 local tokens=("$@") local yaml="access.yml" # Check for fixed, multi, or arbitrary args commands local is_fixed is_multi is_arbit is_fixed="$(yq e ".\"$PERSON\".fixedArgsCommands | has(\"$cmd\")" "$yaml")" is_multi="$(yq e ".\"$PERSON\".multiArgsCommands | has(\"$cmd\")" "$yaml")" is_arbit="$(yq e ".\"$PERSON\".arbitArgsCommands[]" "$yaml" | grep -qx "$cmd" && echo true || echo false)" if [[ "$is_fixed" != "true" && "$is_multi" != "true" && "$is_arbit" != "true" ]]; then echo "ERROR: Command '$cmd' not allowed for $PERSON" >&2 return 1 fi # Exclude flags from positional args for fixed/multi; pass all for arbit local args=() for tok in "${tokens[@]}"; do [[ "$tok" == -* ]] && continue args+=("$tok") done if [[ "$is_fixed" == "true" ]]; then mapfile -t allowed < <(yq e ".\"$PERSON\".fixedArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null) local n_allowed="${#allowed[@]}" if [[ $n_allowed -eq 0 ]]; then # zero-arg command if [[ ${#args[@]} -ne 0 ]]; then echo "ERROR: Command '$cmd' takes no arguments" >&2 return 1 fi else # depth is 1: only one of the allowed choices must be present if [[ ${#args[@]} -ne 1 ]]; then echo "ERROR: Command '$cmd' requires exactly 1 argument: (${allowed[*]})" >&2 return 1 fi local found=0 for want in "${allowed[@]}"; do [[ "${args[0]}" == "$want" ]] && found=1 && break done if [[ $found -eq 0 ]]; then echo "ERROR: Invalid argument '${args[0]}' for '$cmd'; allowed: (${allowed[*]})" >&2 return 1 fi fi return 0 fi if [[ "$is_multi" == "true" ]]; then mapfile -t allowed < <(yq e ".\"$PERSON\".multiArgsCommands.\"$cmd\"[]" "$yaml" 2>/dev/null) local n_allowed="${#allowed[@]}" if [[ ${#args[@]} -lt 1 || ${#args[@]} -gt $n_allowed ]]; then echo "ERROR: Command '$cmd' requires 1 to $n_allowed arguments: (${allowed[*]})" >&2 return 1 fi # Order doesn't matter, but all must be unique and from allowed. # Build a set of allowed args. declare -A allowed_set=() for want in "${allowed[@]}"; do allowed_set["$want"]=1; done declare -A seen=() for a in "${args[@]}"; do if [[ -z "${allowed_set[$a]+_}" ]]; then echo "ERROR: Invalid argument '$a' for '$cmd'; allowed: (${allowed[*]})" >&2 return 1 fi if [[ -n "${seen[$a]+_}" ]]; then echo "ERROR: Duplicate argument '$a' for '$cmd'" >&2 return 1 fi seen["$a"]=1 done return 0 fi if [[ "$is_arbit" == "true" ]]; then # Arbitrary arguments allowed, always valid return 0 fi }