mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 02:27:44 +08:00
62 lines
1.6 KiB
TypeScript
62 lines
1.6 KiB
TypeScript
// PostgreSQL runtime sink for immutable security audit events.
|
|
import {
|
|
SecurityAuditUnavailableError,
|
|
normalizeSecurityAuditRecord,
|
|
type SecurityAuditRecord,
|
|
type SecurityAuditSink,
|
|
} from '@qinglong/runtime-core/security-audit';
|
|
import type { PostgresPool } from '@qinglong/runtime-core';
|
|
|
|
const INSERT_SQL = `
|
|
INSERT INTO "ql3"."security_audit_events" (
|
|
event_id,
|
|
request_id,
|
|
operation_id,
|
|
project_id,
|
|
subject_type,
|
|
subject_id,
|
|
authentication_id,
|
|
outcome,
|
|
reasons,
|
|
project_version,
|
|
binding_version,
|
|
occurred_at_ms
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10, $11, $12)
|
|
`.trim();
|
|
|
|
export class PostgresSecurityAuditRepository implements SecurityAuditSink {
|
|
constructor(private readonly pool: PostgresPool) {
|
|
if (!pool || typeof pool.query !== 'function') {
|
|
throw new TypeError('PostgreSQL security audit pool is invalid');
|
|
}
|
|
}
|
|
|
|
async record(value: SecurityAuditRecord): Promise<void> {
|
|
let record: Readonly<SecurityAuditRecord>;
|
|
try {
|
|
record = normalizeSecurityAuditRecord(value);
|
|
} catch {
|
|
throw new SecurityAuditUnavailableError();
|
|
}
|
|
try {
|
|
await this.pool.query(INSERT_SQL, [
|
|
record.eventId,
|
|
record.requestId,
|
|
record.operationId,
|
|
record.projectId,
|
|
record.subject?.type ?? null,
|
|
record.subject?.id ?? null,
|
|
record.authenticationId,
|
|
record.outcome,
|
|
JSON.stringify(record.reasons),
|
|
record.fence?.projectVersion ?? null,
|
|
record.fence?.bindingVersion ?? null,
|
|
record.occurredAtMs,
|
|
]);
|
|
} catch {
|
|
throw new SecurityAuditUnavailableError();
|
|
}
|
|
}
|
|
}
|