Files
dokkku-google-auth/test/access-list-test.sh
T

134 lines
6.5 KiB
Bash
Executable File

#!/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 <needle> <subcommand> [args...] — asserts the subcommand's
# output (stdout + stderr) contains <needle>. 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"