fix(secrets): expose low-trust user denials in audit log
Associate value-free low-trust denial events with the configured user-secret record so the existing audit API can retrieve them.\n\nCo-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
a25aff0516
commit
c667b9d913
|
|
@ -1686,7 +1686,7 @@ describeEmbeddedPostgres("secretService", () => {
|
|||
ROUTINE_PLAIN: "still-here",
|
||||
};
|
||||
await svc.syncEnvBindingsForTarget(companyId, { targetType: "routine", targetId: "routine-1" }, env);
|
||||
await svc.createCurrentUserSecretValue(companyId, "user-1", {
|
||||
const userSecret = await svc.createCurrentUserSecretValue(companyId, "user-1", {
|
||||
definitionKey: "github_token",
|
||||
value: "user-one-secret",
|
||||
});
|
||||
|
|
@ -1709,15 +1709,10 @@ describeEmbeddedPostgres("secretService", () => {
|
|||
expect(resolved.secretKeys.has("GITHUB_TOKEN")).toBe(false);
|
||||
expect(JSON.stringify(resolved)).not.toContain("user-one-secret");
|
||||
|
||||
const [denialEvent] = await db
|
||||
.select()
|
||||
.from(secretAccessEvents)
|
||||
.where(and(
|
||||
eq(secretAccessEvents.companyId, companyId),
|
||||
eq(secretAccessEvents.userSecretDefinitionId, definition.id),
|
||||
));
|
||||
const [denialEvent] = await svc.listAccessEvents(companyId, userSecret.id);
|
||||
expect(denialEvent).toMatchObject({
|
||||
secretId: null,
|
||||
secretId: userSecret.id,
|
||||
userSecretDefinitionId: definition.id,
|
||||
secretScope: "user",
|
||||
responsibleUserId: "user-1",
|
||||
credentialOwnerUserId: "user-1",
|
||||
|
|
|
|||
|
|
@ -1157,7 +1157,7 @@ export function secretService(db: Db | DbTransaction) {
|
|||
|
||||
async function recordAccessEvent(input: {
|
||||
companyId: string;
|
||||
secretId: string | null;
|
||||
secretId: string;
|
||||
userSecretDefinitionId?: string | null;
|
||||
secretScope?: string | null;
|
||||
version: number | null;
|
||||
|
|
@ -4164,34 +4164,36 @@ export function secretService(db: Db | DbTransaction) {
|
|||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
Array.isArray(context?.allowedBindingIds) &&
|
||||
(!declaration || !context.allowedBindingIds.includes(declaration.id))
|
||||
) {
|
||||
await recordAccessEvent({
|
||||
companyId,
|
||||
secretId: null,
|
||||
userSecretDefinitionId: definition.id,
|
||||
secretScope: "user",
|
||||
version: null,
|
||||
provider: definition.provider as SecretProvider,
|
||||
context: context ? { ...context, responsibleUserId } : undefined,
|
||||
credentialOwnerUserId: responsibleUserId,
|
||||
credentialSubjectType: "user",
|
||||
credentialSubjectId: responsibleUserId,
|
||||
outcome: "failure",
|
||||
errorCode: "binding_not_allowed",
|
||||
}).catch(() => undefined);
|
||||
throw unprocessable(
|
||||
"User secret declaration is outside the active low-trust boundary",
|
||||
{ code: "binding_not_allowed" },
|
||||
);
|
||||
}
|
||||
const secret = await getUserSecretValue({
|
||||
companyId,
|
||||
ownerUserId: responsibleUserId,
|
||||
definitionId: definition.id,
|
||||
});
|
||||
if (
|
||||
Array.isArray(context?.allowedBindingIds) &&
|
||||
(!declaration || !context.allowedBindingIds.includes(declaration.id))
|
||||
) {
|
||||
if (secret) {
|
||||
await recordAccessEvent({
|
||||
companyId,
|
||||
secretId: secret.id,
|
||||
userSecretDefinitionId: definition.id,
|
||||
secretScope: "user",
|
||||
version: null,
|
||||
provider: definition.provider as SecretProvider,
|
||||
context: context ? { ...context, responsibleUserId } : undefined,
|
||||
credentialOwnerUserId: responsibleUserId,
|
||||
credentialSubjectType: "user",
|
||||
credentialSubjectId: responsibleUserId,
|
||||
outcome: "failure",
|
||||
errorCode: "binding_not_allowed",
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
throw unprocessable(
|
||||
"User secret declaration is outside the active low-trust boundary",
|
||||
{ code: "binding_not_allowed" },
|
||||
);
|
||||
}
|
||||
if (!secret) {
|
||||
if (optionalBinding) return null;
|
||||
throw unprocessable("User secret value is not configured", {
|
||||
|
|
|
|||
Loading…
Reference in New Issue