[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
129
electron/plugins/credentialBroker.cjs
Normal file
129
electron/plugins/credentialBroker.cjs
Normal file
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
|
||||
const { PluginRpcError, RPC_ERRORS } = require("./rpcRouter.cjs");
|
||||
const { MAX_SECRET_BYTES, assertSecretRef } = require("./secretStore.cjs");
|
||||
|
||||
function assertCredentialRef(credential) {
|
||||
if (credential?.kind === "secret") {
|
||||
return { kind: "secret", ...assertSecretRef(credential) };
|
||||
}
|
||||
if (
|
||||
!credential
|
||||
|| typeof credential !== "object"
|
||||
|| Array.isArray(credential)
|
||||
|| credential.kind !== "credential"
|
||||
|| typeof credential.id !== "string"
|
||||
|| credential.id.length < 16
|
||||
|| credential.id.length > 256
|
||||
) throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Credential reference is invalid");
|
||||
return { kind: "credential", id: credential.id };
|
||||
}
|
||||
|
||||
function assertLeaseParams(params) {
|
||||
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
||||
throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Credential lease parameters are invalid");
|
||||
}
|
||||
const credential = assertCredentialRef(params.secret);
|
||||
if (typeof params.operationId !== "string" || params.operationId.length < 1 || params.operationId.length > 128) {
|
||||
throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Credential lease operation ID is invalid");
|
||||
}
|
||||
if (typeof params.purpose !== "string" || params.purpose.length < 1 || params.purpose.length > 256) {
|
||||
throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Credential lease purpose is invalid");
|
||||
}
|
||||
if (
|
||||
params.ttlMs !== undefined
|
||||
&& (!Number.isSafeInteger(params.ttlMs) || params.ttlMs < 1 || params.ttlMs > 60_000)
|
||||
) throw new PluginRpcError(RPC_ERRORS.invalidArgument, "Credential lease lifetime is invalid");
|
||||
return {
|
||||
secret: credential,
|
||||
operationId: params.operationId,
|
||||
purpose: params.purpose,
|
||||
...(params.ttlMs === undefined ? {} : { ttlMs: params.ttlMs }),
|
||||
};
|
||||
}
|
||||
|
||||
class PluginCredentialBroker {
|
||||
constructor(options) {
|
||||
this.secretStore = options.secretStore;
|
||||
this.leaseStore = options.leaseStore;
|
||||
this.credentialResolver = options.credentialResolver ?? null;
|
||||
if (this.credentialResolver && (
|
||||
typeof this.credentialResolver.assertReference !== "function"
|
||||
|| typeof this.credentialResolver.resolve !== "function"
|
||||
)) throw new TypeError("Credential resolver must validate and resolve credential references");
|
||||
}
|
||||
|
||||
describeAuthorization(params) {
|
||||
const validated = assertLeaseParams(params);
|
||||
if (validated.secret.kind === "credential") {
|
||||
return {
|
||||
permission: "vault.credentials",
|
||||
resources: [`credential:${validated.secret.id}`],
|
||||
reason: `Use Netcatty credential for ${validated.purpose}`,
|
||||
operationId: validated.operationId,
|
||||
};
|
||||
}
|
||||
return {
|
||||
permission: "secrets",
|
||||
resources: [`secret:${validated.secret.key}`],
|
||||
reason: `Use plugin secret for ${validated.purpose}`,
|
||||
operationId: validated.operationId,
|
||||
};
|
||||
}
|
||||
|
||||
async createLease(params, context) {
|
||||
const validated = assertLeaseParams(params);
|
||||
await context.assertActive();
|
||||
let resolveSecret;
|
||||
if (validated.secret.kind === "credential") {
|
||||
if (!this.credentialResolver) {
|
||||
throw new PluginRpcError(RPC_ERRORS.unavailable, "Netcatty credential access is unavailable");
|
||||
}
|
||||
await this.credentialResolver.assertReference(validated.secret, {
|
||||
pluginId: context.pluginId,
|
||||
runtimeId: context.runtimeId,
|
||||
operationId: validated.operationId,
|
||||
purpose: validated.purpose,
|
||||
signal: context.signal,
|
||||
});
|
||||
await context.assertActive();
|
||||
resolveSecret = async (consumeContext) => {
|
||||
const value = await this.credentialResolver.resolve(
|
||||
validated.secret,
|
||||
Object.freeze({ ...consumeContext, purpose: validated.purpose }),
|
||||
);
|
||||
if (typeof value !== "string" || Buffer.byteLength(value, "utf8") > MAX_SECRET_BYTES) {
|
||||
throw new PluginRpcError(RPC_ERRORS.dataLoss, "Resolved credential is invalid or too large");
|
||||
}
|
||||
return value;
|
||||
};
|
||||
} else {
|
||||
this.secretStore.getRecordByReference(context.pluginId, validated.secret);
|
||||
}
|
||||
return this.leaseStore.issue({
|
||||
pluginId: context.pluginId,
|
||||
runtimeId: context.runtimeId,
|
||||
credential: validated.secret,
|
||||
operationId: validated.operationId,
|
||||
purpose: validated.purpose,
|
||||
ttlMs: validated.ttlMs,
|
||||
signal: context.signal,
|
||||
resolveSecret,
|
||||
});
|
||||
}
|
||||
|
||||
async consumeLease(context, lease, operationId) {
|
||||
await context.assertActive();
|
||||
const value = await this.leaseStore.consume({
|
||||
pluginId: context.pluginId,
|
||||
runtimeId: context.runtimeId,
|
||||
lease,
|
||||
operationId,
|
||||
signal: context.signal,
|
||||
});
|
||||
await context.assertActive();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { PluginCredentialBroker, assertCredentialRef, assertLeaseParams };
|
||||
Reference in New Issue
Block a user