UNPKG

@mesh-tech/mesh-cli

Version:

CLI for Mesh platform development utilities

125 lines (124 loc) 5.87 kB
import { execFileSync } from "node:child_process"; import { logSuccess, logWarn } from "./log.js"; export async function resolveTemporalAuth(tenant, env, platformName = tenant) { const { SSMClient, GetParameterCommand } = await import("@aws-sdk/client-ssm"); const ssm = new SSMClient({ region: process.env.AWS_REGION || "us-east-2" }); const results = {}; async function trySSM(name) { try { const resp = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true })); return resp.Parameter?.Value ?? undefined; } catch { return undefined; } } try { const workerPath = `/mesh-platform/${tenant}/${env}/temporal/worker-service-account-key`; const exchangePath = `/mesh-platform/${tenant}/${env}/temporal/token-exchange-app`; const workerJson = await trySSM(workerPath); if (workerJson) { const worker = JSON.parse(workerJson); results.TEMPORAL_AUTH_CLIENT_ID = worker.clientId; results.TEMPORAL_AUTH_CLIENT_SECRET = worker.clientSecret; } const exchangeJson = await trySSM(exchangePath); if (exchangeJson) { const exchange = JSON.parse(exchangeJson); results.ZITADEL_TOKEN_EXCHANGE_CLIENT_ID = exchange.clientId; results.ZITADEL_TOKEN_EXCHANGE_CLIENT_SECRET = exchange.clientSecret; } const platformJson = await trySSM(`/mesh-platform/${platformName}/${env}/platform/zitadel`); if (platformJson) { const platform = JSON.parse(platformJson); if (platform.endpoint) results.ZITADEL_ISSUER = platform.endpoint; if (platform.projectId) results.ZITADEL_PROJECT_ID = platform.projectId; } if (!results.TEMPORAL_AUTH_CLIENT_ID) { const legacyClientId = await trySSM("/platform/temporal/auth/CLIENT_ID"); const legacyClientSecret = await trySSM("/platform/temporal/auth/CLIENT_SECRET"); if (legacyClientId && legacyClientSecret) { results.TEMPORAL_AUTH_CLIENT_ID = legacyClientId; results.TEMPORAL_AUTH_CLIENT_SECRET = legacyClientSecret; } } if (!results.ZITADEL_ISSUER) { try { const podEnv = execFileSync("kubectl", [ "get", "pods", "-n", `temporal-${env}`, "-l", "app.kubernetes.io/component=frontend", "-o", "jsonpath={.items[0].spec.containers[0].env}", ], { encoding: "utf-8", timeout: 10_000, stdio: ["pipe", "pipe", "pipe"] }).trim(); if (podEnv) { const envVars = JSON.parse(podEnv); for (const v of envVars) { if (v.name === "ZITADEL_ISSUER_URL" && v.value && !results.ZITADEL_ISSUER) { results.ZITADEL_ISSUER = v.value; } if (v.name === "ZITADEL_PROJECT_ID" && v.value && !results.ZITADEL_PROJECT_ID) { results.ZITADEL_PROJECT_ID = v.value; } } } } catch (err) { const detail = err instanceof Error ? err.message : String(err); const stderr = String(err?.stderr ?? ""); if (/\.kube[/\\]config|error loading config file/i.test(`${detail} ${stderr}`)) { logWarn("kubectl could not read a kubeconfig and fell back to ~/.kube/config " + "(macOS blocks it for shells without Full Disk Access). Run inside the " + "mesh dev tmux session, or export KUBECONFIG=$TMPDIR/mesh-dev-sessions/<session>.kubeconfig."); } } } if (results.TEMPORAL_AUTH_CLIENT_ID && results.ZITADEL_ISSUER) { logSuccess(`Temporal auth resolved (client: ${results.TEMPORAL_AUTH_CLIENT_ID})`); } else if (Object.keys(results).length > 0) { logWarn("Partial Temporal auth resolved (some credentials missing)"); } } catch (err) { logWarn(`Could not resolve Temporal auth from SSM: ${err instanceof Error ? err.message : String(err)}`); } return results; } export async function acquireTemporalBearer(vars = {}) { const issuerUrl = vars.ZITADEL_ISSUER ?? process.env.ZITADEL_ISSUER; const projectId = vars.ZITADEL_PROJECT_ID ?? process.env.ZITADEL_PROJECT_ID; const clientId = vars.TEMPORAL_AUTH_CLIENT_ID ?? process.env.TEMPORAL_AUTH_CLIENT_ID; const clientSecret = vars.TEMPORAL_AUTH_CLIENT_SECRET ?? process.env.TEMPORAL_AUTH_CLIENT_SECRET; if (!issuerUrl || !clientId || !clientSecret) { return undefined; } const scopes = ["openid"]; if (projectId) { scopes.push(`urn:zitadel:iam:org:project:id:${projectId}:aud`); if (process.env.TEMPORAL_M2M_SKIP_ROLES_SCOPE !== "true") { scopes.push("urn:zitadel:iam:org:projects:roles"); } } const response = await fetch(`${issuerUrl}/oauth/v2/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "client_credentials", client_id: clientId, client_secret: clientSecret, scope: scopes.join(" "), }), }); if (!response.ok) { const text = await response.text(); throw new Error(`Temporal token acquisition failed: ${response.status} ${response.statusText}${text}`); } const data = (await response.json()); return data.access_token; }