diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index f44cb7b47..3b97147e4 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -26,11 +26,21 @@ jobs: with: node-version: 20 + - name: 🔍 Secret Diagnostics + run: | + echo "Checking available secrets..." + [ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty" + [ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty" + [ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty" + [ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty" + - name: 🔐 Registry Auth env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITEA_PAT: ${{ secrets.GITEA_PAT }} MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }} + MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bash scripts/registry-auth.sh - name: Install dependencies diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index ad3a1ce0a..7089027bc 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -137,11 +137,21 @@ jobs: with: node-version: 20 + - name: 🔍 Secret Diagnostics + run: | + echo "Checking available secrets..." + [ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty" + [ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty" + [ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty" + [ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty" + - name: 🔐 Registry Auth env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITEA_PAT: ${{ secrets.GITEA_PAT }} MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }} + MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bash scripts/registry-auth.sh - name: Install dependencies @@ -336,11 +346,21 @@ jobs: uses: actions/setup-node@v4 with: node-version: 20 + - name: 🔍 Secret Diagnostics + run: | + echo "Checking available secrets..." + [ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty" + [ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty" + [ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty" + [ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty" + - name: 🔐 Registry Auth env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITEA_PAT: ${{ secrets.GITEA_PAT }} MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }} + MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bash scripts/registry-auth.sh - name: Install dependencies run: pnpm install --no-frozen-lockfile diff --git a/.gitea/workflows/qa.yml b/.gitea/workflows/qa.yml index 11e4fa1d4..ae1e46e04 100644 --- a/.gitea/workflows/qa.yml +++ b/.gitea/workflows/qa.yml @@ -27,11 +27,21 @@ jobs: with: node-version: 20 + - name: 🔍 Secret Diagnostics + run: | + echo "Checking available secrets..." + [ -n "${{ secrets.NPM_TOKEN }}" ] && echo "✅ NPM_TOKEN is set" || echo "❌ NPM_TOKEN is empty" + [ -n "${{ secrets.GITEA_PAT }}" ] && echo "✅ GITEA_PAT is set" || echo "❌ GITEA_PAT is empty" + [ -n "${{ secrets.MINTEL_PRIVATE_TOKEN }}" ] && echo "✅ MINTEL_PRIVATE_TOKEN is set" || echo "❌ MINTEL_PRIVATE_TOKEN is empty" + [ -n "${{ secrets.MINTEL_REGISTRY_TOKEN }}" ] && echo "✅ MINTEL_REGISTRY_TOKEN is set" || echo "❌ MINTEL_REGISTRY_TOKEN is empty" + - name: 🔐 Registry Auth env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GITEA_PAT: ${{ secrets.GITEA_PAT }} MINTEL_PRIVATE_TOKEN: ${{ secrets.MINTEL_PRIVATE_TOKEN }} + MINTEL_REGISTRY_TOKEN: ${{ secrets.MINTEL_REGISTRY_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: bash scripts/registry-auth.sh - name: Install dependencies diff --git a/scripts/registry-auth.sh b/scripts/registry-auth.sh index bfe2a9a7c..8cbf383f9 100755 --- a/scripts/registry-auth.sh +++ b/scripts/registry-auth.sh @@ -1,107 +1,104 @@ #!/bin/bash -# Hardened Registry Authentication Discovery & Hardening -# Standard: mmintel/klz-2026 - set -e -REGISTRY_DOMAIN="git.infra.mintel.me" -REGISTRY_PATH="/api/packages/mmintel/npm/" +# --- CONFIGURATION --- +# The registry domain and base path for @mintel packages +DOMAINS=("git.infra.mintel.me" "registry.infra.mintel.me") +REGISTRY_PATH="/api/packages/mmintel/npm" +# Package used for functional connectivity test (URL encoded) TEST_PACKAGE_ENCODED="%40mintel%2Fmail" -TEST_URL="https://${REGISTRY_DOMAIN}${REGISTRY_PATH}${TEST_PACKAGE_ENCODED}" -echo "🔍 Starting robust functional registry discovery..." +echo "🚀 Starting Hardened Registry Authentication Discovery..." -# Collect candidate tokens from environment -# We try them all because NPM_TOKEN might be stale while GITEA_PAT is fresh -CANDIDATES="" -[ -n "$NPM_TOKEN" ] && CANDIDATES="${CANDIDATES} NPM_TOKEN" -[ -n "$GITEA_PAT" ] && CANDIDATES="${CANDIDATES} GITEA_PAT" -[ -n "$MINTEL_PRIVATE_TOKEN" ] && CANDIDATES="${CANDIDATES} MINTEL_PRIVATE_TOKEN" +# --- TOKEN DISCOVERY --- +# We try various candidate secrets. Gitea environment might have different names. +CANDIDATES=( + "NPM_TOKEN" + "GITEA_PAT" + "MINTEL_PRIVATE_TOKEN" + "MINTEL_REGISTRY_TOKEN" + "GITHUB_TOKEN" + "GITEA_TOKEN" +) -if [ -z "$CANDIDATES" ]; then - echo "❌ No registry tokens found in environment (NPM_TOKEN, GITEA_PAT, MINTEL_PRIVATE_TOKEN are empty)." - exit 1 +WORKING_TOKEN="" +WORKING_DOMAIN="" + +for DOMAIN in "${DOMAINS[@]}"; do + echo "🔍 Testing connectivity to ${DOMAIN}..." + + for VAR_NAME in "${CANDIDATES[@]}"; do + TOKEN_VAL="${!VAR_NAME}" + [ -z "${TOKEN_VAL}" ] && continue + + echo " Testing ${VAR_NAME} (len: ${#TOKEN_VAL})..." + + # Method 1: Bearer + STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${TOKEN_VAL}" "https://${DOMAIN}${REGISTRY_PATH}/${TEST_PACKAGE_ENCODED}" || echo "000") + if [ "$STATUS" == "200" ]; then + echo " ✅ ${VAR_NAME} verified via Bearer on ${DOMAIN}" + WORKING_TOKEN="${TOKEN_VAL}" + WORKING_DOMAIN="${DOMAIN}" + break 2 + fi + + # Method 2: Basic (Token as user, empty password) + AUTH_BASIC=$(echo -n "${TOKEN_VAL}:" | base64) + STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Basic ${AUTH_BASIC}" "https://${DOMAIN}${REGISTRY_PATH}/${TEST_PACKAGE_ENCODED}" || echo "000") + if [ "$STATUS" == "200" ]; then + echo " ✅ ${VAR_NAME} verified via Basic on ${DOMAIN}" + WORKING_TOKEN="${TOKEN_VAL}" + WORKING_DOMAIN="${DOMAIN}" + break 2 + fi + + # Method 3: Basic (User 'token', Token as password - Gitea standard) + AUTH_GITEA=$(echo -n "token:${TOKEN_VAL}" | base64) + STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Basic ${AUTH_GITEA}" "https://${DOMAIN}${REGISTRY_PATH}/${TEST_PACKAGE_ENCODED}" || echo "000") + if [ "$STATUS" == "200" ]; then + echo " ✅ ${VAR_NAME} verified via Gitea-Token-Basic on ${DOMAIN}" + WORKING_TOKEN="${TOKEN_VAL}" + WORKING_DOMAIN="${DOMAIN}" + break 2 + fi + done +done + +if [ -z "${WORKING_TOKEN}" ]; then + echo "⚠️ ALL functional tests failed for all domains." + if [ -n "${NPM_TOKEN}" ]; then + echo "Falling back to NPM_TOKEN (unverified) and git.infra.mintel.me" + WORKING_TOKEN="${NPM_TOKEN}" + WORKING_DOMAIN="git.infra.mintel.me" + else + echo "❌ CRITICAL: No usable token found and NPM_TOKEN is empty." + exit 1 + fi fi -AUTH_TYPE="None" -WORKING_TOKEN="" +# --- .npmrc GENERATION --- +# We generate a .npmrc that matches the local pattern exactly to avoid resolution drift. +# Local pattern: +# @mintel:registry=https://git.infra.mintel.me/api/packages/mmintel/npm +# //git.infra.mintel.me/api/packages/mmintel/npm/:_authToken=${TOKEN} -# Function to test connectivity -check_url() { - local label=$1 - local auth_header=$2 - local url=$3 - - # Using -k for internal cert resilience - local code=$(curl -s -k -o /tmp/reg_body -w "%{http_code}" -H "$auth_header" "$url") - if [ "$code" = "200" ]; then - return 0 - else - local body=$(cat /tmp/reg_body | head -c 100) - echo " $label failed ($code): $body" - return 1 - fi -} +cat > .npmrc << EOF +@mintel:registry=https://${WORKING_DOMAIN}${REGISTRY_PATH} +//${WORKING_DOMAIN}${REGISTRY_PATH}/:_authToken=${WORKING_TOKEN} +//${WORKING_DOMAIN}${REGISTRY_PATH}/:always-auth=true +EOF -for CANDIDATE_NAME in $CANDIDATES; do - TOKEN_VAL=$(eval echo "\$$CANDIDATE_NAME") - echo "Testing token from $CANDIDATE_NAME (length: ${#TOKEN_VAL})..." - - # 1. Try Bearer - if check_url "Bearer" "Authorization: Bearer ${TOKEN_VAL}" "${TEST_URL}"; then - echo "✅ Working Bearer token found in $CANDIDATE_NAME." - AUTH_TYPE="Bearer" - WORKING_TOKEN="${TOKEN_VAL}" - break - fi - - # 2. Try Basic (token:x-oauth-basic) - Gitea Standard - # Encode token:x-oauth-basic - BASIC_AUTH=$(echo -n "${TOKEN_VAL}:x-oauth-basic" | base64 | tr -d '\n') - if check_url "Basic (token:x-oauth-basic)" "Authorization: Basic ${BASIC_AUTH}" "${TEST_URL}"; then - echo "✅ Working Basic (token:x-oauth-basic) found in $CANDIDATE_NAME." - AUTH_TYPE="Bearer" # Still use _authToken - WORKING_TOKEN="${TOKEN_VAL}" - break - fi - - # 3. Try Basic (token:empty) - BASIC_AUTH_EMPTY=$(echo -n "${TOKEN_VAL}:" | base64 | tr -d '\n') - if check_url "Basic (token:)" "Authorization: Basic ${BASIC_AUTH_EMPTY}" "${TEST_URL}"; then - echo "✅ Working Basic (token:) found in $CANDIDATE_NAME." - AUTH_TYPE="Bearer" - WORKING_TOKEN="${TOKEN_VAL}" - break - fi - - # 4. Try Gitea 'token' header - if check_url "Token Header" "Authorization: token ${TOKEN_VAL}" "${TEST_URL}"; then - echo "✅ Working Gitea Token header found in $CANDIDATE_NAME." - AUTH_TYPE="Bearer" - WORKING_TOKEN="${TOKEN_VAL}" - break +# Add secondary domain if we found one working +for DOMAIN in "${DOMAINS[@]}"; do + if [ "${DOMAIN}" != "${WORKING_DOMAIN}" ]; then + cat >> .npmrc << EOF +//${DOMAIN}${REGISTRY_PATH}/:_authToken=${WORKING_TOKEN} +//${DOMAIN}${REGISTRY_PATH}/:always-auth=true +EOF fi done -# Generate .npmrc -echo "Generating .npmrc..." -{ - echo "always-auth=true" - echo "@mintel:registry=https://${REGISTRY_DOMAIN}${REGISTRY_PATH}" - - if [ "$AUTH_TYPE" != "None" ]; then - echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH}:_authToken=${WORKING_TOKEN}" - echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH}:always-auth=true" - # Fallback without trailing slash for some versions of npm/pnpm - CLEAN_PATH=$(echo "${REGISTRY_PATH}" | sed 's/\/$//') - echo "//${REGISTRY_DOMAIN}${CLEAN_PATH}:_authToken=${WORKING_TOKEN}" - echo "✅ .npmrc hardened with verified $CANDIDATE_NAME." - else - echo "❌ ALL functional tests failed. Falling back to NPM_TOKEN (unverified)." - echo "//${REGISTRY_DOMAIN}${REGISTRY_PATH}:_authToken=${NPM_TOKEN}" - fi -} > .npmrc - -# Debug summary -echo "Final .npmrc configuration (masked):" -cat .npmrc | sed 's/_authToken=.*/_authToken=********/' +echo "✅ .npmrc hardened with verified token on ${WORKING_DOMAIN}." +echo "--- Generated .npmrc (masked) ---" +sed 's/_authToken=.*/_authToken=********/' .npmrc +echo "--------------------------------"