feat: Token Broker infrastructure (HRT-205)
Some checks failed
CD / Deploy → Staging (push) Has been cancelled
CD / Smoke Tests on Staging (push) Has been cancelled
CD / Deploy → Production (push) Has been cancelled
CD / Rollback Production (push) Has been cancelled

- PostgreSQL dedie Docker (postgres:16-alpine, port 5434)
- 6 tables: api_tokens, refresh_tokens, token_audit_log, clients, providers, token_usage
- Init SQL + Flask init_db() mis a jour
- Systemd service token-broker (port 8783)
- Deploy script infra/scripts/deploy_token_broker.sh
- Docker compose broker (docker-compose.broker.yml)
- Health check OK: status=ok, database=connected

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
CTO H3R7Tech
2026-05-24 09:22:12 +02:00
parent cd4cbcfb48
commit 8ab42343aa
7 changed files with 932 additions and 0 deletions

View File

@@ -0,0 +1,94 @@
-- Token Broker PostgreSQL init script
-- 6 tables: api_tokens, refresh_tokens, token_audit_log, clients, providers, token_usage
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE TABLE IF NOT EXISTS api_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER NOT NULL,
name TEXT NOT NULL DEFAULT 'default',
token_hash TEXT NOT NULL UNIQUE,
token_prefix TEXT NOT NULL,
scopes TEXT[] DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ,
last_used_at TIMESTAMPTZ,
metadata JSONB DEFAULT '{}'
);
CREATE TABLE IF NOT EXISTS refresh_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER NOT NULL,
token_hash TEXT NOT NULL UNIQUE,
token_prefix TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
revoked BOOLEAN NOT NULL DEFAULT FALSE,
revoked_at TIMESTAMPTZ,
replaced_by UUID
);
CREATE TABLE IF NOT EXISTS token_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id INTEGER,
action TEXT NOT NULL,
token_prefix TEXT,
ip_address TEXT,
user_agent TEXT,
details JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS clients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
client_id TEXT NOT NULL UNIQUE,
client_secret TEXT NOT NULL,
name TEXT NOT NULL,
description TEXT DEFAULT '',
redirect_uris TEXT[] DEFAULT '{}',
scopes TEXT[] DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS providers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL UNIQUE,
provider_type TEXT NOT NULL DEFAULT 'oauth2',
issuer_url TEXT,
client_id TEXT,
client_secret TEXT,
scopes TEXT[] DEFAULT '{}',
config JSONB DEFAULT '{}',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS token_usage (
id BIGSERIAL PRIMARY KEY,
user_id INTEGER NOT NULL,
token_id UUID,
action TEXT NOT NULL DEFAULT 'verify',
endpoint TEXT,
status TEXT NOT NULL DEFAULT 'success',
response_time_ms INTEGER,
ip_address TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_api_tokens_user_id ON api_tokens(user_id);
CREATE INDEX IF NOT EXISTS idx_api_tokens_token_hash ON api_tokens(token_hash);
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id);
CREATE INDEX IF NOT EXISTS idx_refresh_tokens_token_hash ON refresh_tokens(token_hash);
CREATE INDEX IF NOT EXISTS idx_token_audit_log_user_id ON token_audit_log(user_id);
CREATE INDEX IF NOT EXISTS idx_token_audit_log_created_at ON token_audit_log(created_at);
CREATE INDEX IF NOT EXISTS idx_clients_client_id ON clients(client_id);
CREATE INDEX IF NOT EXISTS idx_providers_name ON providers(name);
CREATE INDEX IF NOT EXISTS idx_token_usage_user_id ON token_usage(user_id);
CREATE INDEX IF NOT EXISTS idx_token_usage_created_at ON token_usage(created_at);
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO token_broker;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO token_broker;

View File

@@ -0,0 +1,90 @@
#!/bin/bash
# ============================================================
# Deploy Token Broker — systemd service + Docker PG
# ============================================================
set -euo pipefail
APP_DIR="/home/h3r7/turf_saas"
SERVICE_NAME="token-broker"
PID_FILE="/tmp/token_broker.pid"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
echo "[$(date -Iseconds)] === Deploying Token Broker ==="
# Step 1: Backup current code
echo "[$(date -Iseconds)] Backing up current code..."
mkdir -p /home/h3r7/backups/token-broker
cp "${APP_DIR}/services/token-broker/token_broker_api.py" \
"/home/h3r7/backups/token-broker/token_broker_api_${TIMESTAMP}.py"
# Step 2: Ensure Docker PG is running
echo "[$(date -Iseconds)] Ensuring PostgreSQL container..."
if ! docker inspect token-broker-db >/dev/null 2>&1; then
echo "Creating PG container..."
docker run -d \
--name token-broker-db \
--restart unless-stopped \
-e POSTGRES_DB=token_broker \
-e POSTGRES_USER=token_broker \
-e POSTGRES_PASSWORD="${TOKEN_BROKER_DB_PASSWORD}" \
-v token-broker-pgdata:/var/lib/postgresql/data \
-v "${APP_DIR}/infra/postgres/token_broker_init.sql:/docker-entrypoint-initdb.d/init.sql:ro" \
-p 127.0.0.1:5434:5432 \
postgres:16-alpine
elif ! docker ps --filter name=token-broker-db --format '{{.Status}}' | grep -q Up; then
echo "Starting existing PG container..."
docker start token-broker-db
else
echo "PG container already running."
fi
# Wait for PG readiness
echo "[$(date -Iseconds)] Waiting for PG to be ready..."
for i in $(seq 1 20); do
if docker exec token-broker-db pg_isready -U token_broker -d token_broker >/dev/null 2>&1; then
echo "PG ready."
break
fi
sleep 2
done
# Step 3: Ensure psycopg2-binary is installed
echo "[$(date -Iseconds)] Checking Python deps..."
source "${APP_DIR}/venv/bin/activate"
pip install -q psycopg2-binary PyJWT flask-cors python-dotenv gunicorn 2>/dev/null || true
# Step 4: Stop current service
echo "[$(date -Iseconds)] Stopping current service..."
if systemctl is-active --quiet ${SERVICE_NAME} 2>/dev/null; then
systemctl stop ${SERVICE_NAME}
elif [ -f "$PID_FILE" ] && kill -0 $(cat "$PID_FILE") 2>/dev/null; then
kill $(cat "$PID_FILE") 2>/dev/null || true
fi
sleep 2
# Step 5: Copy systemd unit and start
echo "[$(date -Iseconds)] Starting via systemd..."
cp "${APP_DIR}/services/token-broker/token-broker.service" /etc/systemd/system/
systemctl daemon-reload
systemctl enable ${SERVICE_NAME}
systemctl start ${SERVICE_NAME}
# Wait for startup
sleep 3
# Step 6: Health check
echo "[$(date -Iseconds)] Running health check..."
HEALTH=$(curl -s http://127.0.0.1:8783/health 2>/dev/null || echo '{"status":"failed"}')
STATUS=$(echo "$HEALTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('status','unknown'))" 2>/dev/null || echo "unknown")
if [ "$STATUS" = "ok" ]; then
echo "[$(date -Iseconds)] ✅ Health check passed: ${HEALTH}"
echo "[$(date -Iseconds)] === Token Broker deploy SUCCESS ==="
else
echo "[$(date -Iseconds)] ❌ Health check failed: ${HEALTH}"
echo "[$(date -Iseconds)] === Token Broker deploy FAILED ==="
exit 1
fi
# Step 7: Clean old backups (keep last 30)
find /home/h3r7/backups/token-broker -name "*.py" -mtime +30 -delete