test(auth): gate local and OIDC authentication release
This commit is contained in:
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
expected_node=${THT_EXPECTED_NODE_VERSION:-v24.16.0}
|
||||
actual_node=$(node --version)
|
||||
|
||||
if [[ "$actual_node" != "$expected_node" ]]; then
|
||||
printf 'authentication smoke requires Node %s; found %s\n' "$expected_node" "$actual_node" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
for command in go openssl; do
|
||||
command -v "$command" >/dev/null 2>&1 || {
|
||||
printf 'authentication smoke requires %s\n' "$command" >&2
|
||||
exit 2
|
||||
}
|
||||
done
|
||||
|
||||
playwright="$root/frontend/node_modules/.bin/playwright"
|
||||
[[ -x "$playwright" ]] || {
|
||||
echo "authentication smoke requires frontend dependencies (run npm ci in frontend)" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
temporary_root=$(mktemp -d "${TMPDIR:-/tmp}/thothii-authentication-smoke.XXXXXX")
|
||||
trap 'rm -rf "$temporary_root"' EXIT HUP INT TERM
|
||||
log="$temporary_root/playwright.log"
|
||||
output="$temporary_root/playwright"
|
||||
mkdir -p "$output"
|
||||
sentinel="task15-sentinel-$(openssl rand -hex 24)"
|
||||
export THT_TASK15_SENTINEL="$sentinel"
|
||||
|
||||
sanitize_failure_log() {
|
||||
sed -E \
|
||||
-e "s/${sentinel}/[redacted]/g" \
|
||||
-e 's#https?://[^[:space:])]+#[url]#g' \
|
||||
-e 's/(THT_[A-Z_]+|PI_[A-Z_]+|AUTH_MODE)=[^[:space:]]+/\1=[redacted]/g' \
|
||||
"$log" | tail -n 100 >&2
|
||||
}
|
||||
|
||||
if ! (
|
||||
cd "$root/frontend"
|
||||
THT_E2E_AUTH_STACK=1 "$playwright" test e2e/auth.spec.ts --workers=1 --output="$output"
|
||||
) >"$log" 2>&1; then
|
||||
echo "authentication smoke: hermetic browser suite failed" >&2
|
||||
sanitize_failure_log
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if rg -a -Fq -- "$sentinel" "$log" "$output"; then
|
||||
echo "authentication smoke: sentinel appeared in retained test output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'authentication smoke: hermetic OIDC/browser suite passed on Node %s\n' "$actual_node"
|
||||
@@ -18,10 +18,56 @@ trap cleanup EXIT HUP INT TERM
|
||||
printf '%s\n' '{"zai":{"type":"api_key","key":"fixture-native-auth-key"}}' >"$tmp/pi-auth.json"
|
||||
printf '%s\n' 'THT_MODEL_API_KEY=fixture-model-api-key' >"$tmp/thothii.secrets"
|
||||
chmod 0600 "$tmp/pi-auth.json" "$tmp/thothii.secrets"
|
||||
auth_config="$tmp/auth"
|
||||
mkdir "$auth_config"
|
||||
chmod 0700 "$auth_config"
|
||||
printf '%s\n' \
|
||||
'version: 1' \
|
||||
'mode: local' \
|
||||
'publicUrl: http://127.0.0.1:8080' \
|
||||
'local:' \
|
||||
' usersFile: users.yaml' \
|
||||
>"$auth_config/auth.yaml"
|
||||
node - "$auth_config/users.yaml" <<'NODE'
|
||||
const { argon2 } = require("node:crypto");
|
||||
const { writeFileSync } = require("node:fs");
|
||||
|
||||
const message = Buffer.from("fixture-local-password", "utf8");
|
||||
const nonce = Buffer.from([...Array(16).keys()]);
|
||||
argon2("argon2id", {
|
||||
message,
|
||||
nonce,
|
||||
memory: 65_536,
|
||||
parallelism: 1,
|
||||
tagLength: 32,
|
||||
passes: 3,
|
||||
}, (error, digest) => {
|
||||
message.fill(0);
|
||||
nonce.fill(0);
|
||||
if (error || !digest) throw error ?? new Error("fixture password hash failed");
|
||||
const salt = Buffer.from([...Array(16).keys()]).toString("base64").replaceAll("=", "");
|
||||
const hash = digest.toString("base64").replaceAll("=", "");
|
||||
writeFileSync(process.argv[2], [
|
||||
"version: 1",
|
||||
"users:",
|
||||
" - id: 00000000-0000-4000-8000-000000000001",
|
||||
" username: fixture-user",
|
||||
" displayName: Fixture user",
|
||||
` passwordHash: $argon2id$v=19$m=65536,t=3,p=1$${salt}$${hash}`,
|
||||
" roles:",
|
||||
" - user",
|
||||
" enabled: true",
|
||||
" authRevision: 1",
|
||||
"",
|
||||
].join("\\n"), { mode: 0o600 });
|
||||
});
|
||||
NODE
|
||||
chmod 0600 "$auth_config/auth.yaml" "$auth_config/users.yaml"
|
||||
printf '%s\n' \
|
||||
'THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git' \
|
||||
"PI_AUTH_FILE=$tmp/pi-auth.json" \
|
||||
"THT_SECRETS_FILE=$tmp/thothii.secrets" \
|
||||
"THT_AUTH_CONFIG_ROOT=$auth_config" \
|
||||
'THOTH_CORE_HTTP_PORT=0' \
|
||||
'THOTH_HTTP_PORT=0' \
|
||||
>"$tmp/local.env"
|
||||
|
||||
@@ -8,12 +8,18 @@ trap 'rm -rf "$tmp"' EXIT HUP INT TERM
|
||||
auth_file="$tmp/auth.json"
|
||||
printf '%s\n' '{}' >"$auth_file"
|
||||
chmod 0600 "$auth_file"
|
||||
auth_config="$tmp/auth"
|
||||
mkdir "$auth_config"
|
||||
chmod 0700 "$auth_config"
|
||||
printf '%s\n' 'mode: local' >"$auth_config/auth.yaml"
|
||||
chmod 0600 "$auth_config/auth.yaml"
|
||||
secrets_file="$tmp/thothii.secrets"
|
||||
printf '%s\n' 'THT_MODEL_API_KEY=fixture-model-api-key' >"$secrets_file"
|
||||
chmod 0600 "$secrets_file"
|
||||
|
||||
rendered=$(THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git \
|
||||
PI_AUTH_FILE="$auth_file" THT_SECRETS_FILE="$secrets_file" docker compose config)
|
||||
PI_AUTH_FILE="$auth_file" THT_SECRETS_FILE="$secrets_file" \
|
||||
THT_AUTH_CONFIG_ROOT="$auth_config" docker compose config)
|
||||
printf '%s\n' "$rendered" | grep -q "source: $auth_file"
|
||||
printf '%s\n' "$rendered" | grep -q 'target: /home/thoth/.pi/agent/auth.json'
|
||||
printf '%s\n' "$rendered" | grep -A4 'target: /home/thoth/.pi/agent/auth.json' \
|
||||
@@ -33,6 +39,7 @@ fi
|
||||
|
||||
dev_rendered=$(THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git \
|
||||
PI_AUTH_FILE="$auth_file" THT_SECRETS_FILE="$secrets_file" \
|
||||
THT_AUTH_CONFIG_ROOT="$auth_config" \
|
||||
docker compose --env-file deploy/env/local.env.example -f docker-compose.dev.yml config)
|
||||
printf '%s\n' "$dev_rendered" | grep -q "source: $auth_file"
|
||||
printf '%s\n' "$dev_rendered" | grep -q 'target: /home/thoth/.pi/agent/auth.json'
|
||||
|
||||
@@ -5,13 +5,18 @@ cd "$(dirname "$0")/.."
|
||||
|
||||
tmp_bundle=$(mktemp)
|
||||
tmp_auth=$(mktemp)
|
||||
trap 'rm -f "$tmp_bundle" "$tmp_auth"' EXIT HUP INT TERM
|
||||
tmp_auth_config=$(mktemp -d)
|
||||
trap 'rm -f "$tmp_bundle" "$tmp_auth"; rm -rf "$tmp_auth_config"' EXIT HUP INT TERM
|
||||
printf '%s\n' 'THT_MODEL_API_KEY=test-model' >"$tmp_bundle"
|
||||
chmod 0600 "$tmp_bundle"
|
||||
printf '%s\n' '{}' >"$tmp_auth"
|
||||
chmod 0600 "$tmp_auth"
|
||||
chmod 0700 "$tmp_auth_config"
|
||||
printf '%s\n' 'mode: local' >"$tmp_auth_config/auth.yaml"
|
||||
chmod 0600 "$tmp_auth_config/auth.yaml"
|
||||
export THT_SECRETS_FILE="$tmp_bundle"
|
||||
export PI_AUTH_FILE="$tmp_auth"
|
||||
export THT_AUTH_CONFIG_ROOT="$tmp_auth_config"
|
||||
export THT_WORKSPACE_GIT_REMOTE=https://git.example.invalid/platform/thoth-workspaces.git
|
||||
|
||||
config_json=$(docker compose -f compose.yaml -f deploy/compose.preprocess.yaml --profile preprocess config --format json)
|
||||
|
||||
@@ -9,7 +9,12 @@ fixture="$(mktemp -d "$tmp_parent/thoth-server-pi-state.XXXXXX")"
|
||||
trap 'rm -rf "$fixture"' EXIT HUP INT TERM
|
||||
|
||||
pi_state="$fixture/empty pi state"
|
||||
auth_config="$fixture/auth"
|
||||
mkdir -p "$pi_state"
|
||||
mkdir -p "$auth_config"
|
||||
chmod 0700 "$auth_config"
|
||||
printf 'mode: local\n' >"$auth_config/auth.yaml"
|
||||
chmod 0600 "$auth_config/auth.yaml"
|
||||
"$root/scripts/prepare-server-pi-state.sh" "$pi_state" "$(id -u)" "$(id -g)"
|
||||
|
||||
for target in auth.json models.json settings.json; do
|
||||
@@ -40,6 +45,7 @@ THOTH_SERVER_BIND=127.0.0.1
|
||||
THOTH_HTTP_PORT=0
|
||||
PI_AUTH_FILE=$fixture/pi-auth.json
|
||||
THT_SECRETS_FILE=$fixture/thothii.secrets
|
||||
THT_AUTH_CONFIG_ROOT=$auth_config
|
||||
THT_DATA_ROOT=$fixture/data
|
||||
THT_PI_STATE_ROOT=$pi_state
|
||||
THT_WORKSPACE_REGISTRY_ROOT=$fixture/workspace-registry
|
||||
|
||||
Reference in New Issue
Block a user