mcp --http: --broker-profiles (store keys from the broker vault, memory only), --read-only, project by folder name under the roots
This commit is contained in:
parent
164ba390dd
commit
a108aae53f
6 changed files with 121 additions and 10 deletions
|
|
@ -325,3 +325,35 @@ export async function playPreflight(command: string, root: string, pkg: string |
|
|||
if (o.dry) { for (const g of gaps) o.warn(`${g.message}. Fix: ${g.fix}`); return gaps; }
|
||||
throw new Error(`${command} stopped before any call to Google Play:\n${gapsText(gaps)}`);
|
||||
}
|
||||
|
||||
export type StoreKind = 'asc-api-key' | 'play-service-account';
|
||||
|
||||
/**
|
||||
* A store credential of one profile from the broker's vault (GET /api/store-credentials/<profile>/<kind>, a key with
|
||||
* the store-credentials scope): { secret, metadata }. For a long-running server (`mcp --http --broker-profiles`) that
|
||||
* keeps no key files; null when the broker has none for that profile. Never logged.
|
||||
*/
|
||||
export async function brokerStoreCredential(cfg: BrokerConfig, profile: string, kind: StoreKind): Promise<{ secret: string; metadata: Record<string, unknown> } | null> {
|
||||
const a = await request(cfg, 'GET', `/api/store-credentials/${encodeURIComponent(profile)}/${kind}`);
|
||||
if (a.status === 404) return null;
|
||||
if (a.status === 401 || a.status === 403) throw new BrokerRefused(`credential broker ${host(cfg)} refused store credentials for ${profile} (${a.status} ${errOf(a)}): the key needs the store-credentials scope and the profile`, a.status);
|
||||
const j = a.json as { secret?: unknown; metadata?: unknown } | null;
|
||||
if (a.status !== 200 || !j || typeof j.secret !== 'string') throw new BrokerUnavailable(`credential broker ${host(cfg)} -> ${a.status} for ${profile}/${kind}`);
|
||||
return { secret: j.secret, metadata: j.metadata && typeof j.metadata === 'object' ? (j.metadata as Record<string, unknown>) : {} };
|
||||
}
|
||||
|
||||
/** The env a child process needs to use these profiles from the broker (STOREBOUND_PROFILE_<NAME>_<KEY>, inline PEM /
|
||||
* JSON; see credentials.ts). Profiles the broker has nothing for are left out. */
|
||||
export async function brokerProfileEnv(cfg: BrokerConfig, profiles: string[]): Promise<Record<string, string>> {
|
||||
const env: Record<string, string> = {};
|
||||
const name = (p: string, k: string) => `STOREBOUND_PROFILE_${p.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_${k}`;
|
||||
await Promise.all(profiles.map(async p => {
|
||||
const [asc, play] = await Promise.all([brokerStoreCredential(cfg, p, 'asc-api-key'), brokerStoreCredential(cfg, p, 'play-service-account')]);
|
||||
if (asc) {
|
||||
const { issuerId, keyId } = asc.metadata;
|
||||
if (typeof issuerId === 'string' && typeof keyId === 'string') Object.assign(env, { [name(p, 'ASC_P8')]: asc.secret, [name(p, 'ASC_KEY_ID')]: keyId, [name(p, 'ASC_ISSUER_ID')]: issuerId });
|
||||
}
|
||||
if (play) env[name(p, 'PLAY_SERVICE_ACCOUNT')] = play.secret;
|
||||
}));
|
||||
return env;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue