feat: convert secret-sources into a plugin (B20)

This commit is contained in:
LukeGus committed 2026-09-24 16:20:20 -05:00
1 parent 2ac7779eda
commit f4f26a9b8b
72 files changed
+18843 -933

No files matched your search

+15
View File
@@ -1257,6 +1257,21 @@ export interface PluginCredentials {
hostId: number,
protocol: PluginHostProtocol,
) => Promise<PluginProtocolTarget | null>;
/**
* Registers this plugin as the resolver for "<scheme>://..." references in
* a host's secret fields (secret-sources registers "op" for
* "op://vault/item/field"). Needs auth:provide, like registerSshAuthProvider.
*
* `resolve` runs once per host resolution, for the user the field's owner
* resolves as (the host owner, or the recipient for a personal override),
* and its result is cached briefly by the connect pipeline. A scheme with no
* registered resolver fails the connect with a message naming the plugin
* that would provide it.
*/
registerSecretResolver: (
scheme: string,
resolve: (userId: string, reference: string) => Promise<string>,
) => void;
}
/**
+11 -3
View File
@@ -257,6 +257,8 @@ export interface PluginAuthContribution {
loginMethods?: string[];
/** Second factor ids. */
secondFactors?: string[];
/** Schemes this plugin resolves for "<scheme>://..." secret references, e.g. "op". */
secretSchemes?: string[];
}
export interface PluginContributions {
@@ -790,11 +792,16 @@ function validateAuthContribution(value: unknown, errors: string[]): void {
}
rejectUnknown(
value,
["sshAuthTypes", "loginMethods", "secondFactors"],
["sshAuthTypes", "loginMethods", "secondFactors", "secretSchemes"],
"contributes.auth",
errors,
);
for (const key of ["sshAuthTypes", "loginMethods", "secondFactors"]) {
for (const key of [
"sshAuthTypes",
"loginMethods",
"secondFactors",
"secretSchemes",
]) {
const list = value[key];
if (list === undefined) continue;
if (!Array.isArray(list)) {
@@ -1343,7 +1350,8 @@ export function parseManifest(raw: unknown): ParsedManifest {
const contributesAuth =
(auth?.sshAuthTypes?.length ?? 0) +
(auth?.loginMethods?.length ?? 0) +
(auth?.secondFactors?.length ?? 0) >
(auth?.secondFactors?.length ?? 0) +
(auth?.secretSchemes?.length ?? 0) >
0;
if (contributesAuth && !manifest.capabilities.includes("auth:provide")) {
errors.push(
+16
View File
@@ -126,6 +126,11 @@ export interface FakeAuthRegistrations {
secondFactors: PluginSecondFactor[];
/** "<userId>:<factorId>" for every recorded enrolment. */
enrollments: Set<string>;
/** Secret schemes registered through ctx.credentials.registerSecretResolver. */
secretResolvers: Map<
string,
(userId: string, reference: string) => Promise<string>
>;
}
export interface FakePluginContext {
@@ -336,6 +341,7 @@ export function createFakeContext(
loginMethods: [],
secondFactors: [],
enrollments: new Set(),
secretResolvers: new Map(),
};
const sshClient = options.sshClient ?? {};
let actor = options.actor;
@@ -800,6 +806,12 @@ export function createFakeContext(
credentialReads.push({ hostId, protocol });
return options.protocolTargets?.[`${hostId}:${protocol}`] ?? null;
},
registerSecretResolver: (scheme, resolve) => {
auth.secretResolvers.set(scheme, resolve);
disposals.push(() => {
auth.secretResolvers.delete(scheme);
});
},
},
notify: {
@@ -1223,6 +1235,10 @@ export function createMockCtx(
require("credentials:read");
return ctx.credentials.resolveHostProtocol(hostId, protocol);
},
registerSecretResolver: (scheme, resolve) => {
require("auth:provide");
ctx.credentials.registerSecretResolver(scheme, resolve);
},
},
notify: {