feat: add validate_command_access.sh with its tests

This commit is contained in:
Pallav Vasa 2025-05-17 15:12:18 +00:00
parent ba052d78d4
commit e6fff0c044
2 changed files with 130 additions and 0 deletions

View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -e
cat >access.yml <<EOF
pallav:
fixedArgsCommands:
build:
- base
- workspace
- all
clean:
status:
multiArgsCommands:
remove:
- palak
- param
- darshan
EOF
source ./validate_command_access.sh
testcase() {
local desc="$1"
shift
if validate_command pallav "$@"; then
echo "PASS: $desc"
else
echo "FAIL: $desc"
fi
}
testcase "build base (valid)" build base
testcase "build all (valid)" build all
testcase "build base workspace (invalid)" build base workspace || true
testcase "build (no arg, invalid)" build || true
testcase "clean (zero-arg, valid)" clean
testcase "clean with arg (invalid)" clean foo || true
testcase "remove palak (valid)" remove palak
testcase "remove param palak (valid, any order)" remove param palak
testcase "remove palak param darshan (valid, any order)" remove palak param darshan
testcase "remove (no arg, invalid)" remove || true
testcase "remove foo (invalid)" remove foo || true
testcase "remove palak palak (duplicate, invalid)" remove palak palak || true
testcase "status (zero-arg, valid)" status
testcase "status foo (invalid)" status foo || true

View File

@ -0,0 +1,83 @@
#!/usr/bin/env bash
validate_command() {
local PERSON="$1"
local cmd="$2"
shift 2
local tokens=("$@")
local yaml="access.yml"
# Check if fixedArgsCommands.<cmd> exists
local is_fixed
is_fixed="$(yq e ".\"$PERSON\".fixedArgsCommands | has(\"$cmd\")" "$yaml")"
# Check if multiArgsCommands.<cmd> exists
local is_multi
is_multi="$(yq e ".\"$PERSON\".multiArgsCommands | has(\"$cmd\")" "$yaml")"
if [[ "$is_fixed" != "true" && "$is_multi" != "true" ]]; then
echo "ERROR: Command '$cmd' not allowed for $PERSON" >&2
return 1
fi
# Exclude flags from positional args
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
[[ -z "${allowed_set[$a]}" ]] && {
echo "ERROR: Invalid argument '$a' for '$cmd'; allowed: (${allowed[*]})" >&2
return 1
}
[[ -n "${seen[$a]}" ]] && {
echo "ERROR: Duplicate argument '$a' for '$cmd'" >&2
return 1
}
seen["$a"]=1
done
return 0
fi
}