openclaw/src/secrets/auth-profiles-scan.ts

124 lines
3.2 KiB
TypeScript

import { isNonEmptyString, isRecord } from "./shared.js";
import { listAuthProfileSecretTargetEntries } from "./target-registry.js";
export type AuthProfileCredentialType = "api_key" | "token";
type AuthProfileFieldSpec = {
valueField: string;
refField: string;
};
type ApiKeyCredentialVisit = {
kind: "api_key";
profileId: string;
provider: string;
profile: Record<string, unknown>;
valueField: string;
refField: string;
value: unknown;
refValue: unknown;
};
type TokenCredentialVisit = {
kind: "token";
profileId: string;
provider: string;
profile: Record<string, unknown>;
valueField: string;
refField: string;
value: unknown;
refValue: unknown;
};
type OauthCredentialVisit = {
kind: "oauth";
profileId: string;
provider: string;
profile: Record<string, unknown>;
hasAccess: boolean;
hasRefresh: boolean;
};
export type AuthProfileCredentialVisit =
| ApiKeyCredentialVisit
| TokenCredentialVisit
| OauthCredentialVisit;
function getAuthProfileFieldName(pathPattern: string): string {
const segments = pathPattern.split(".").filter(Boolean);
return segments[segments.length - 1] ?? "";
}
const AUTH_PROFILE_FIELD_SPEC_BY_TYPE = (() => {
const defaults: Record<AuthProfileCredentialType, AuthProfileFieldSpec> = {
api_key: { valueField: "key", refField: "keyRef" },
token: { valueField: "token", refField: "tokenRef" },
};
for (const target of listAuthProfileSecretTargetEntries()) {
if (!target.authProfileType) {
continue;
}
defaults[target.authProfileType] = {
valueField: getAuthProfileFieldName(target.pathPattern),
refField:
target.refPathPattern !== undefined
? getAuthProfileFieldName(target.refPathPattern)
: defaults[target.authProfileType].refField,
};
}
return defaults;
})();
export function getAuthProfileFieldSpec(type: AuthProfileCredentialType): AuthProfileFieldSpec {
return AUTH_PROFILE_FIELD_SPEC_BY_TYPE[type];
}
export function* iterateAuthProfileCredentials(
profiles: Record<string, unknown>,
): Iterable<AuthProfileCredentialVisit> {
for (const [profileId, value] of Object.entries(profiles)) {
if (!isRecord(value) || !isNonEmptyString(value.provider)) {
continue;
}
const provider = String(value.provider);
if (value.type === "api_key") {
const spec = getAuthProfileFieldSpec("api_key");
yield {
kind: "api_key",
profileId,
provider,
profile: value,
valueField: spec.valueField,
refField: spec.refField,
value: value[spec.valueField],
refValue: value[spec.refField],
};
continue;
}
if (value.type === "token") {
const spec = getAuthProfileFieldSpec("token");
yield {
kind: "token",
profileId,
provider,
profile: value,
valueField: spec.valueField,
refField: spec.refField,
value: value[spec.valueField],
refValue: value[spec.refField],
};
continue;
}
if (value.type === "oauth") {
yield {
kind: "oauth",
profileId,
provider,
profile: value,
hasAccess: isNonEmptyString(value.access),
hasRefresh: isNonEmptyString(value.refresh),
};
}
}
}