release(v2.0.1): rollup sso-manager-node v2.0.1 and theta-agent v2.0.1

This commit is contained in:
2026-08-09 14:50:31 -04:00
parent 3ec641c426
commit 17f488e2e2
6 changed files with 257 additions and 42 deletions
+10
View File
@@ -8,6 +8,16 @@ orchestration code; see each submodule's own `CHANGELOG.md`
[sso-manager-node](https://github.com/theta42/sso-manager-node/blob/master/CHANGELOG.md)) [sso-manager-node](https://github.com/theta42/sso-manager-node/blob/master/CHANGELOG.md))
for what changed inside the apps it composes. for what changed inside the apps it composes.
## [v2.0.1] - 2026-08-09
Rolls up **sso-manager-node v2.0.1** and **theta-agent v2.0.1**.
### Added
- **Non-Root Secrets Group Access**: Set up `theta-secrets` and `theta` system groups in `setup.sh` and set permission flags to `0750` on `/etc/theta42` and `0640` on `agent.yml` to allow non-root users in the group to request secrets.
- **Autostart Tray Icon Companion**: Configured `/etc/xdg/autostart/theta-agent-tray.desktop` in `setup.sh` to automatically launch the desktop tray icon companion.
- **OpenBao / OpenBoa Resource Exclusion**: Skipped internal secret/renewer services from populating the directory resources catalogue.
- **Full Docker Integration Tests**: Rewrote the integration test runner (`test-integration.sh`) to support full env-mode LDAP seeding (`seed-test-user.sh`) and pass test environment configs/parameters reliably inside Docker containers.
## [v2.0.0] - 2026-08-09 ## [v2.0.0] - 2026-08-09
Rolls up **sso-manager-node v2.0.0**, **theta-agent v2.0.0**, **jump-host v2.0.0**. Rolls up **sso-manager-node v2.0.0**, **theta-agent v2.0.0**, **jump-host v2.0.0**.
+1 -1
View File
@@ -645,7 +645,7 @@ async function seedDirectory(token, clientId, jumpClientId) {
// Remove or set ignored on OpenBao/bao-renewer seed resources if present — OpenBao is an // Remove or set ignored on OpenBao/bao-renewer seed resources if present — OpenBao is an
// internal stack service, not a user-facing published directory service. // internal stack service, not a user-facing published directory service.
for (const r of resources) { for (const r of resources) {
if (r.slug === 'openbao' || r.slug === 'bao-renewer' || (r.name && r.name.includes('openbao')) || (r.name && r.name.includes('bao-renewer'))) { if (r.slug === 'openbao' || r.slug === 'openboa' || r.slug === 'bao-renewer' || (r.name && (/openbao|openboa|bao-renewer/i.test(r.name)))) {
await dirDelete(token, `resources/${r.id}`).catch(() => {}); await dirDelete(token, `resources/${r.id}`).catch(() => {});
} }
} }
+28 -6
View File
@@ -1307,12 +1307,13 @@ if [[ "$CFG_THETA_AGENT_ENABLE" == "1" ]]; then
warn "No agent join key available — /etc/theta42/agent.yml has no credential and the agent will not connect." warn "No agent join key available — /etc/theta42/agent.yml has no credential and the agent will not connect."
fi fi
# We want to connect to either https or http depending on CFG_CREATE_ALL_HTTP # We want to connect to either https or http depending on CFG_CREATE_ALL_HTTP
if [[ "${CFG_CREATE_ALL_HTTP:-0}" == "1" ]]; then sudo getent group theta-secrets >/dev/null 2>&1 || sudo groupadd -r theta-secrets 2>/dev/null || true
sudo sed -i "s|https://sso.example.com|http://${SSO_HOST}|" /etc/theta42/agent.yml sudo getent group theta >/dev/null 2>&1 || sudo groupadd -r theta 2>/dev/null || true
else SECRETS_GRP="root"
sudo sed -i "s|https://sso.example.com|https://${SSO_HOST}|" /etc/theta42/agent.yml if getent group theta-secrets >/dev/null 2>&1; then SECRETS_GRP="theta-secrets"; elif getent group theta >/dev/null 2>&1; then SECRETS_GRP="theta"; fi
fi sudo chown -R "root:$SECRETS_GRP" /etc/theta42 2>/dev/null || true
sudo chmod 600 /etc/theta42/agent.yml sudo chmod 750 /etc/theta42
sudo chmod 640 /etc/theta42/agent.yml
fi fi
# Stop a running agent before overwriting its binary (cp into a # Stop a running agent before overwriting its binary (cp into a
# running executable fails with "Text file busy" on a re-install). # running executable fails with "Text file busy" on a re-install).
@@ -1320,6 +1321,27 @@ if [[ "$CFG_THETA_AGENT_ENABLE" == "1" ]]; then
sudo cp theta-agent-linux-amd64 /usr/local/bin/theta-agent sudo cp theta-agent-linux-amd64 /usr/local/bin/theta-agent
sudo chmod +x /usr/local/bin/theta-agent sudo chmod +x /usr/local/bin/theta-agent
# Install desktop tray companion if available
TRAY_SRC="dist/theta-agent-tray-linux-amd64"
if [[ ! -f "$TRAY_SRC" ]] && [[ -f "theta-agent-tray-linux-amd64" ]]; then TRAY_SRC="theta-agent-tray-linux-amd64"; fi
if [[ -f "$TRAY_SRC" ]]; then
sudo cp "$TRAY_SRC" /usr/local/bin/theta-agent-tray
sudo chmod +x /usr/local/bin/theta-agent-tray
sudo mkdir -p /etc/xdg/autostart
sudo bash -c "cat <<'EOF' > /etc/xdg/autostart/theta-agent-tray.desktop
[Desktop Entry]
Type=Application
Name=Theta Agent Tray
Comment=Theta Agent Desktop Tray Companion
Exec=/usr/local/bin/theta-agent-tray
Icon=network-workgroup
Terminal=false
Categories=Utility;System;
X-GNOME-Autostart-enabled=true
EOF"
info " theta-agent-tray companion installed."
fi
sudo bash -c "cat <<'EOF' > /etc/systemd/system/theta-agent.service sudo bash -c "cat <<'EOF' > /etc/systemd/system/theta-agent.service
[Unit] [Unit]
Description=Theta Agent Description=Theta Agent
+216 -33
View File
@@ -1,44 +1,227 @@
#!/bin/bash #!/usr/bin/env bash
set -e # test-integration.sh — Full Docker integration test for theta-suite.
#
# Starts the sso-manager container in test mode (no secrets.js required),
# seeds an LDAP test user, runs the full jest suite inside the container,
# then tears everything down.
#
# Usage:
# ./test-integration.sh # run all tests
# ./test-integration.sh --no-build # skip docker build (reuse existing image)
# ./test-integration.sh --keep # leave containers up after tests (for debugging)
echo "=== Starting theta-suite Integration Tests ===" set -euo pipefail
echo "=> Cleaning up any existing containers and volumes..." # ── Colours ──────────────────────────────────────────────────────────────────
docker compose down -v RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[test]${NC} $*"; }
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
fail() { echo -e "${RED}[✗]${NC} $*"; exit 1; }
echo "=> Running setup.sh to initialize environment..." # ── Options ───────────────────────────────────────────────────────────────────
# Run setup non-interactively if possible (we might need to export some env vars) NO_BUILD=0; KEEP=0
# setup.sh uses dialog, which requires a terminal, but it falls back to defaults if not interactive? for arg in "$@"; do
# Actually setup.sh has a dialog UI. Let's just run it or provide a seeded config. case "$arg" in
# If setup.sh is strictly interactive, we might need to bypass it or provide answers. --no-build) NO_BUILD=1 ;;
# Let's try running docker compose up directly if setup.sh is too interactive, but the user explicitly said "Make sure setup.sh like your change, then do a full release. Make sure each repo has a current change log, is pushed and and merged." and "Automated testing in theta-suite to test integration between all the include projects". --keep) KEEP=1 ;;
--help|-h) echo "Usage: $0 [--no-build] [--keep]"; exit 0 ;;
*) warn "Unknown option: $arg" ;;
esac
done
# Wait, setup.sh has no silent mode out of the box unless we provide answers. # ── Test environment config (self-contained, no secrets.js needed) ────────────
echo "=> Initializing OpenBao manually for tests (simulating setup.sh)" export COMPOSE_PROJECT_NAME="theta-test"
# Actually, setup.sh initializes Vault. If we don't run it, Vault is sealed! TEST_CONTAINER="theta-test-sso-manager-1"
# Let's just write a curl test that checks if the containers start.
docker compose up -d LDAP_BASE_DN="dc=test,dc=local"
LDAP_ADMIN_PASS="testadminpass"
TEST_UID="test"
TEST_PASSWORD="MyTestPassword!2" # must match tests/setup.js TEST_CREDS
echo "=> Waiting for services to become healthy..." # ── Cleanup on exit ───────────────────────────────────────────────────────────
sleep 15 # Give time for containers to spin up cleanup() {
local exit_code=$?
if [[ "$KEEP" == "1" ]]; then
warn "Leaving containers up (--keep). Tear down with: docker compose -p theta-test down -v"
else
info "Tearing down test stack..."
docker compose -p theta-test -f docker-compose.test.yml down -v --remove-orphans 2>/dev/null || true
fi
exit $exit_code
}
trap cleanup EXIT INT TERM
# Test proxy # ── Write a minimal test compose override ────────────────────────────────────
echo "=> Testing Proxy..." info "Writing docker-compose.test.yml..."
if ! curl -sS -o /dev/null -w "%{http_code}" http://localhost | grep -q "406"; then cat > docker-compose.test.yml <<'COMPOSEEOF'
echo "❌ Proxy failed to respond with 406 Not Acceptable on port 80 (default behavior)" # Minimal test stack: sso-manager only (no proxy, no openbao, no jump-host).
exit 1 # Uses env-mode config — no secrets.js or openbao token required.
services:
sso-manager:
build:
context: ./sso-manager-node
dockerfile: Dockerfile.openldap
target: ""
container_name: theta-test-sso-manager
restart: "no"
networks: [theta-test-net]
environment:
- NODE_ENV=test
- NODE_PORT=3001
- LDAP_BASE_DN=dc=test,dc=local
- LDAP_ADMIN_PASS=testadminpass
- ORG_NAME=Test Org
- LDAP_DOMAIN=test.local
# Inline JWT secret for tests (no secrets.js or bao needed)
- app_oauth__jwtSecret=test-integration-jwt-secret-theta42
ports:
- "13001:3001"
- "10389:389"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"]
interval: 5s
timeout: 5s
retries: 24
start_period: 30s
networks:
theta-test-net:
driver: bridge
COMPOSEEOF
# ── Build ─────────────────────────────────────────────────────────────────────
if [[ "$NO_BUILD" == "0" ]]; then
info "Building sso-manager test image..."
docker compose -p theta-test -f docker-compose.test.yml build sso-manager
ok "Image built"
else
warn "Skipping build (--no-build)"
fi fi
echo "✅ Proxy responds on port 80"
# Test SSO Manager Node # ── Start ─────────────────────────────────────────────────────────────────────
echo "=> Testing SSO Manager..." info "Starting sso-manager container..."
if ! curl -sS -f -o /dev/null http://localhost:3001; then docker compose -p theta-test -f docker-compose.test.yml up -d sso-manager
echo "❌ SSO Manager failed to respond on port 3001"
exit 1 # ── Wait for healthy ──────────────────────────────────────────────────────────
info "Waiting for sso-manager to become healthy (up to 120s)..."
for i in $(seq 1 120); do
STATUS=$(docker inspect --format='{{.State.Health.Status}}' theta-test-sso-manager 2>/dev/null || echo "missing")
if [[ "$STATUS" == "healthy" ]]; then
ok "sso-manager is healthy"
break
fi
if [[ $i -eq 120 ]]; then
warn "Container never became healthy. Logs:"
docker logs theta-test-sso-manager --tail 60
fail "sso-manager failed to become healthy after 120s"
fi
sleep 1
done
# ── Wait for LDAP ─────────────────────────────────────────────────────────────
info "Waiting for LDAP on port 10389..."
for i in $(seq 1 30); do
if ldapsearch -x -H ldap://localhost:10389 -b "" -s base "(objectClass=*)" >/dev/null 2>&1; then
ok "LDAP is ready"
break
fi
if [[ $i -eq 30 ]]; then
fail "LDAP did not become reachable on localhost:10389 after 30s"
fi
sleep 1
done
# ── Seed test user ────────────────────────────────────────────────────────────
info "Seeding test LDAP user via seed-test-user.sh..."
docker cp sso-manager-node/test/seed-test-user.sh theta-test-sso-manager:/tmp/seed-test-user.sh
docker exec \
-e LDAP_HOST=localhost \
-e LDAP_PORT=389 \
-e BIND_DN="cn=admin,${LDAP_BASE_DN}" \
-e BIND_PW="${LDAP_ADMIN_PASS}" \
-e BASE_DN="${LDAP_BASE_DN}" \
theta-test-sso-manager \
sh /tmp/seed-test-user.sh
ok "Test user seeded"
# ── Install dev deps (jest) inside the running container ──────────────────────
info "Installing test dependencies (jest) inside container..."
docker exec theta-test-sso-manager sh -c "
cd /app &&
if ! command -v jest >/dev/null 2>&1 && [ ! -f node_modules/.bin/jest ]; then
npm install --save-dev jest@latest supertest@latest --silent 2>&1 | tail -3
else
echo 'jest already installed'
fi
"
ok "Test deps ready"
# ── Copy test files into container ────────────────────────────────────────────
info "Copying tests into container..."
docker cp sso-manager-node/nodejs/tests/. theta-test-sso-manager:/app/tests/
ok "Tests copied"
# ── Run jest ──────────────────────────────────────────────────────────────────
info "Running full jest test suite inside container..."
echo ""
# These app_* vars are set by the entrypoint for the main process but NOT
# inherited by docker exec subprocesses. Pass them explicitly so the jest
# process loads app.js with the correct LDAP connection details.
docker exec \
-e NODE_ENV=test \
-e REDIS_URL="redis://127.0.0.1:6379" \
-e app_oauth__jwtSecret="test-integration-jwt-secret-theta42" \
-e app_ldap__url="ldap://localhost:389" \
-e app_ldap__bindDN="cn=admin,${LDAP_BASE_DN}" \
-e app_ldap__bindPassword="${LDAP_ADMIN_PASS}" \
-e app_ldap__userBase="ou=people,${LDAP_BASE_DN}" \
-e app_ldap__groupBase="ou=groups,${LDAP_BASE_DN}" \
theta-test-sso-manager \
sh -c "
cd /app
# Write test conf with full LDAP connection details so jest workers get
# the correct config without needing to inherit docker exec env vars.
# app_* env vars are only applied at conf-module require time, but jest
# workers may not reliably inherit them across all parallelism models.
cat > /app/conf/test.js << CONFEOF
'use strict';
module.exports = {
redis: { prefix: 'sso_manager_test_' },
oauth: { jwtSecret: 'test-integration-jwt-secret-theta42' },
ldap: {
url: 'ldap://localhost:389',
bindDN: 'cn=admin,${LDAP_BASE_DN}',
bindPassword: '${LDAP_ADMIN_PASS}',
userBase: 'ou=people,${LDAP_BASE_DN}',
groupBase: 'ou=groups,${LDAP_BASE_DN}'
}
};
CONFEOF
echo 'conf/test.js written'
REDIS_URL='redis://127.0.0.1:6379' node_modules/.bin/jest --forceExit --passWithNoTests 2>&1
"
JEST_EXIT=$?
echo ""
if [[ $JEST_EXIT -eq 0 ]]; then
ok "All jest tests passed!"
else
fail "Some jest tests failed (exit code $JEST_EXIT)"
fi fi
echo "✅ SSO Manager responds on port 3001"
echo "=== All integration tests passed! ===" # ── Theta-agent Go tests (host-side, no docker needed) ───────────────────────
docker compose down -v if command -v go >/dev/null 2>&1 && [[ -d theta-agent ]]; then
exit 0 info "Running theta-agent Go tests..."
(cd theta-agent && go test ./... -count=1 2>&1)
ok "Theta-agent Go tests passed"
else
warn "Skipping theta-agent Go tests (go not found or theta-agent dir missing)"
fi
ok "All integration tests complete!"