#!/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 -> 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 -> 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)"