minecraft-server/scripts/test-default-perms.sh

145 lines
4.4 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# scripts/test-default-perms.sh
#
# Snapshot the things that influence what a default-rank player on
# racked.ru can do. Designed for diffing across config changes.
#
# Output: a deterministic dump (sorted where possible) of:
# - LuckPerms `default` group nodes
# - ProAntiTab `default` group whitelist
# - PAT global command whitelist
# - Plugin enable order from the most recent boot
# - Aliases defined in commands.yml
#
# Runs from the host (laptop or nullstone). Reads files out of the
# minecraft-mc container; parses YAML / JSON on the host (PyYAML and
# python3 are not present in the Paper container image).
#
# Usage (from any host with SSH access to nullstone):
# bash scripts/test-default-perms.sh > snapshot.txt
# bash scripts/test-default-perms.sh --remote > snapshot.txt # explicit ssh
#
# Then `diff` two snapshots taken before/after a change.
#
# Pairs with docs/PLAYER-SMOKE-TEST.md.
set -euo pipefail
CONTAINER="${MC_CONTAINER:-minecraft-mc}"
DATA="${MC_DATA:-/data}"
SSH_TARGET="${MC_SSH:-user@192.168.0.100}"
# If we're running on the box itself (docker is local), skip SSH.
mode="local"
if ! command -v docker >/dev/null 2>&1 || ! docker ps --format '{{.Names}}' 2>/dev/null | grep -qx "$CONTAINER"; then
mode="ssh"
fi
if [[ "${1:-}" == "--remote" ]]; then
mode="ssh"
fi
# read_file <path-inside-container> -> stdout = file contents
read_file() {
local path="$1"
if [[ "$mode" == "ssh" ]]; then
ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER cat $path"
else
docker exec "$CONTAINER" cat "$path"
fi
}
# rcon <command> -> stdout = response (LP exports go to file, not stdout)
rcon() {
local cmd="$1"
if [[ "$mode" == "ssh" ]]; then
ssh -o BatchMode=yes "$SSH_TARGET" "echo '$cmd' | docker exec -i $CONTAINER rcon-cli"
else
echo "$cmd" | docker exec -i "$CONTAINER" rcon-cli
fi
}
# docker_logs -> stdout = full container log
docker_logs() {
if [[ "$mode" == "ssh" ]]; then
ssh -o BatchMode=yes "$SSH_TARGET" "docker logs $CONTAINER 2>&1"
else
docker logs "$CONTAINER" 2>&1
fi
}
# read_lp_export -> stdout = JSON (decompressed) of latest snapshot
read_lp_export() {
rcon 'lp export default-perms-snapshot' >/dev/null
sleep 1
local path="${DATA}/plugins/LuckPerms/default-perms-snapshot.json.gz"
if [[ "$mode" == "ssh" ]]; then
ssh -o BatchMode=yes "$SSH_TARGET" "docker exec $CONTAINER bash -c 'gunzip -c $path'"
else
docker exec "$CONTAINER" bash -c "gunzip -c $path"
fi
}
heading() {
printf '\n========== %s ==========\n' "$1"
}
heading "Mode"
echo "$mode (target: ${SSH_TARGET}, container: ${CONTAINER})"
# ---------- LuckPerms default-group export ----------
heading "LuckPerms default-group nodes"
read_lp_export | python3 -c '
import json, sys
try:
data = json.load(sys.stdin)
except Exception as e:
print(f"(failed to parse LP export: {e})")
sys.exit(0)
default = data.get("groups", {}).get("default", {}).get("nodes", [])
for node in sorted(default, key=lambda n: (n.get("type",""), n.get("key",""))):
t = node.get("type","?")
k = node.get("key","?")
v = node.get("value","?")
print(f"{t:12s} {k} = {v}")
'
# ---------- ProAntiTab whitelist ----------
heading "ProAntiTab — global commands"
read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c '
import sys, yaml
data = yaml.safe_load(sys.stdin) or {}
for cmd in sorted(data.get("global", {}).get("commands", []) or []):
print(cmd)
'
heading "ProAntiTab — default-group commands"
read_file "${DATA}/plugins/ProAntiTab/storage.yml" | python3 -c '
import sys, yaml
data = yaml.safe_load(sys.stdin) or {}
default = (data.get("groups", {}) or {}).get("default", {}) or {}
priority = default.get("priority", "?")
print("# priority: " + str(priority))
for cmd in sorted(default.get("commands", []) or []):
print(cmd)
'
# ---------- commands.yml aliases ----------
heading "commands.yml aliases"
read_file "${DATA}/commands.yml" | python3 -c '
import sys, yaml
data = yaml.safe_load(sys.stdin) or {}
aliases = data.get("aliases", {}) or {}
for name in sorted(aliases.keys()):
target = aliases[name]
print(f"{name}: {target}")
'
# ---------- Plugin enable order (latest boot) ----------
heading "Plugin enable order — latest boot"
docker_logs \
| grep -E "Enabling [A-Z]" \
| tail -25 \
| sed -E 's/^\[[0-9:]+\] \[Server thread\/INFO\]: //'
heading "Snapshot complete: $(date -Is)"