#!/usr/bin/env bash # Exercises the allow/deny list helpers and the google-auth:allow / :unallow / # :deny / :undeny subcommands against a fake dokku layout, and checks that the # lists reach the env file the auth service reads. set -eo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" WORK="$(mktemp -d)" trap 'rm -rf "$WORK"' EXIT # Fake dokku host layout. export DOKKU_ROOT="$WORK/dokku-root" export DOKKU_LIB_ROOT="$WORK/dokku-lib" export PLUGIN_CORE_AVAILABLE_PATH="$WORK/nonexistent" # force built-in fallbacks GLOBAL="$DOKKU_LIB_ROOT/data/google-auth/global" mkdir -p "$GLOBAL" # Stub docker so the subcommands see the service as not running and never touch # a real container. This test is about list handling, not container management. mkdir -p "$WORK/bin" printf '#!/bin/sh\nexit 1\n' >"$WORK/bin/docker" chmod +x "$WORK/bin/docker" export PATH="$WORK/bin:$PATH" # shellcheck disable=SC1091 source "$ROOT/functions" fail() { echo "FAIL: $*" 1>&2 exit 1 } ga() { local sub="$1" shift "$ROOT/subcommands/$sub" "google-auth:$sub" "$@" } # expect_output [args...] — asserts the subcommand's # output (stdout + stderr) contains . Collects the output first rather # than piping into grep, which would SIGPIPE the writer under pipefail. expect_output() { local needle="$1" out shift out="$(ga "$@" 2>&1 || true)" grep -qF "$needle" <<<"$out" || fail "expected '$needle' in google-auth:$1 output, got: $out" } # --- entry validation and classification --- fn-ga-validate-list-entry "guest@partner.com" || fail "address should be a valid entry" fn-ga-validate-list-entry "signal.org" || fail "domain should be a valid entry" fn-ga-validate-list-entry "a@b.com,c@d.com" && fail "comma should be rejected (it splits the env var)" fn-ga-validate-list-entry "not a domain" && fail "whitespace should be rejected" fn-ga-validate-list-entry "" && fail "empty entry should be rejected" [[ "$(fn-ga-allow-entry-kind "signal.org")" == "domain" ]] || fail "bare domain misclassified" [[ "$(fn-ga-allow-entry-kind "@signal.org")" == "domain" ]] || fail "@domain misclassified" [[ "$(fn-ga-allow-entry-kind "guest@partner.com")" == "email" ]] || fail "address misclassified" echo "ok: entry validation and classification" # --- list helpers --- fn-ga-global-list-add allowed-emails "a@x.com" fn-ga-global-list-add allowed-emails "a@x.com" # idempotent [[ "$(fn-ga-global-list-count allowed-emails)" -eq 1 ]] || fail "duplicate add should be a no-op" fn-ga-global-list-contains allowed-emails "a@x.com" || fail "contains should find the entry" fn-ga-global-list-contains allowed-emails "a@x.co" && fail "contains should match whole lines only" fn-ga-global-list-remove allowed-emails "a@x.com" [[ "$(fn-ga-global-list-count allowed-emails)" -eq 0 ]] || fail "remove should empty the list" fn-ga-global-list-remove allowed-emails "nope@x.com" # missing entry is not an error echo "ok: list helpers" # --- allow --- ga allow >/dev/null || fail "listing an empty allow list should succeed" ga allow Signal.org @Example.com Guest@Partner.com >/dev/null grep -qx "signal.org" "$GLOBAL/allowed-domains" || fail "bare domain should be stored lowercased" grep -qx "example.com" "$GLOBAL/allowed-domains" || fail "@domain should be stored without the @" grep -qx "guest@partner.com" "$GLOBAL/allowed-emails" || fail "address should be stored lowercased" ga allow "a@b.com,c@d.com" 2>/dev/null && fail "allow should reject an entry with a comma" ga allow "localhost" 2>/dev/null && fail "allow should reject a domain with no dot" [[ "$(fn-ga-global-list-count allowed-domains)" -eq 2 ]] || fail "rejected entries should not be stored" echo "ok: allow" # --- deny wins, and contradictions are surfaced --- ga deny Former@Signal.org >/dev/null grep -qx "former@signal.org" "$GLOBAL/denied-emails" || fail "deny should store the address lowercased" ga deny signal.org 2>/dev/null && fail "deny should reject a bare domain" ga deny guest@partner.com >/dev/null # guest@partner.com is on both lists now; allow must say deny wins. expect_output "deny list" allow guest@partner.com echo "ok: deny" # --- undeny --- ga undeny Guest@Partner.com >/dev/null fn-ga-global-list-contains denied-emails "guest@partner.com" && fail "undeny should remove the address" expect_output "was not on the deny list" undeny never@denied.com ga deny stranger@elsewhere.com >/dev/null expect_output "still cannot sign in" undeny stranger@elsewhere.com echo "ok: undeny" # --- unallow, including the lockout guardrail --- ga unallow @Example.com >/dev/null fn-ga-global-list-contains allowed-domains "example.com" && fail "unallow should remove the domain" expect_output "was not on the allow list" unallow absent@nowhere.com # signal.org + guest@partner.com remain; removing both (with a duplicate to # check de-duplication) must be refused, and must not change anything. before="$(cat "$GLOBAL/allowed-domains" "$GLOBAL/allowed-emails")" ga unallow signal.org guest@partner.com SIGNAL.ORG 2>/dev/null && fail "unallow should refuse to empty the allow list" [[ "$(cat "$GLOBAL/allowed-domains" "$GLOBAL/allowed-emails")" == "$before" ]] || fail "a refused unallow must leave the lists untouched" ga unallow guest@partner.com >/dev/null || fail "unallow should still remove a non-final entry" [[ "$(fn-ga-global-list-count allowed-domains)" -eq 1 ]] || fail "signal.org should remain allowed" echo "ok: unallow and lockout guardrail" # --- configure's replace-the-list flags stay consistent with the above --- "$ROOT/subcommands/configure" google-auth:configure \ --allow-domain signal.org --deny-email one@signal.org --deny-email two@signal.org >/dev/null 2>&1 || true [[ "$(fn-ga-global-list-count denied-emails)" -eq 2 ]] || fail "--deny-email should replace the deny list" "$ROOT/subcommands/configure" google-auth:configure --clear-deny-emails >/dev/null 2>&1 || true [[ "$(fn-ga-global-list-count denied-emails)" -eq 0 ]] || fail "--clear-deny-emails should empty the deny list" echo "ok: configure flags" # --- the lists reach the service env file --- ga deny former@signal.org >/dev/null ga allow guest@partner.com >/dev/null fn-ga-write-env-file ENV_FILE="$DOKKU_LIB_ROOT/data/google-auth/service.env" grep -qx "GOOGLE_AUTH_ALLOWED_DOMAINS=signal.org" "$ENV_FILE" || fail "env file missing allowed domains" grep -qx "GOOGLE_AUTH_ALLOWED_EMAILS=guest@partner.com" "$ENV_FILE" || fail "env file missing allowed emails" grep -qx "GOOGLE_AUTH_DENIED_EMAILS=former@signal.org" "$ENV_FILE" || fail "env file missing denied emails" echo "ok: service env file" echo "ALL ACCESS LIST TESTS PASSED"