feat(audit): extend AuditAction enum + audit logging on alerts + expense dedup
- AuditAction gains password_change, portal_invite/activate/reset variants, send, view. AuditLogParams.ipAddress/userAgent now optional so background jobs and internal helpers can log without faking values. - alerts.service.dismissAlert/acknowledgeAlert now write action='update' rows with metadata.kind so the audit log differentiates the two state changes. - expense-dedup.service.clearDuplicate/mergeDuplicate accept userId and write action='update'/'merge' rows respectively. Routes pass ctx.userId. Audit gaps surfaced by audit-pass-#2: 6 services bypassed audit_logs entirely. This commit closes 2 of them; portal-auth lands in a later commit alongside the email-template-override work that already touches the same file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,14 @@ export type AuditAction =
|
||||
| 'revoke_invite'
|
||||
| 'resend_invite'
|
||||
| 'request_gdpr_export'
|
||||
| 'send_gdpr_export';
|
||||
| 'send_gdpr_export'
|
||||
| 'password_change'
|
||||
| 'portal_invite'
|
||||
| 'portal_activate'
|
||||
| 'portal_password_reset_request'
|
||||
| 'portal_password_reset'
|
||||
| 'send'
|
||||
| 'view';
|
||||
|
||||
/**
|
||||
* Common shape passed to service functions so they can stamp audit logs and
|
||||
@@ -43,8 +50,10 @@ export interface AuditLogParams {
|
||||
oldValue?: Record<string, unknown>;
|
||||
newValue?: Record<string, unknown>;
|
||||
metadata?: Record<string, unknown>;
|
||||
ipAddress: string;
|
||||
userAgent: string;
|
||||
/** Optional. Services that don't have request context (e.g. background
|
||||
* jobs, internal helpers) may omit. */
|
||||
ipAddress?: string;
|
||||
userAgent?: string;
|
||||
}
|
||||
|
||||
const SENSITIVE_FIELDS = new Set(['email', 'phone', 'password', 'credentials_enc', 'token']);
|
||||
@@ -104,8 +113,8 @@ export async function createAuditLog(params: AuditLogParams): Promise<void> {
|
||||
oldValue: maskSensitiveFields(params.oldValue) ?? null,
|
||||
newValue: maskSensitiveFields(params.newValue) ?? null,
|
||||
metadata: params.metadata ?? null,
|
||||
ipAddress: params.ipAddress,
|
||||
userAgent: params.userAgent,
|
||||
ipAddress: params.ipAddress ?? null,
|
||||
userAgent: params.userAgent ?? null,
|
||||
});
|
||||
} catch (err) {
|
||||
// Strip old/new values from the log to avoid secondary exposure of the data
|
||||
|
||||
Reference in New Issue
Block a user