fix(salary): stop RLS from failing vab/parental absence registration (#1568)
Migration 20260517135000 rewrote the franvaro-specifikationsnummer trigger functions to insert audit rows into salary_absence_franvaro_audit, a table with RLS enabled and zero policies, while leaving the functions SECURITY INVOKER (its comment claimed implicit SECURITY DEFINER, which is false in Postgres). Every vab/parental insert from role authenticated (dashboard absence POST, web /pending approval, in-app Assistenten chat) then failed with 42501, surfaced as a generic 500, and left no diagnosable trace. - New migration 20260813120000: ALTER both trigger functions to SECURITY DEFINER with search_path pinned to public, pg_temp. No RLS policy is added on the audit table: trigger/service-only writes stay the design intent. - mapInsertError: 42501 now maps to the new bilingual DB_PERMISSION_DENIED code instead of INTERNAL_ERROR, and 23514 is split so only the 24h-cap trigger's 'Total tid' message becomes ABSENCE_HOURS_CONFLICT; other CHECK violations map to VALIDATION_ERROR. - commitRegisterAbsence/commitDeleteAbsence: log the underlying PG details and persist the sanitized structured code in result_data.error_code so the next failure is traceable from the op row. - Dashboard absence route: only ABSENCE_HOURS_CONFLICT passes details.message through to the client; every other code shows the registry Swedish message instead of raw Postgres text. - New pg-real regression test locks the authenticated-role parental/vab insert path, the shared per-month specnummer sequence, the audit rows, and idempotent upsert retries. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,16 @@ const GENERIC: Record<string, StructuredErrorEntry> = {
|
||||
message_sv: 'Du har inte behörighet att utföra denna åtgärd.',
|
||||
message_en: 'Insufficient permissions.',
|
||||
},
|
||||
// A Postgres privilege/RLS denial (42501) on a write the application
|
||||
// expected to succeed: a server-side configuration bug (e.g. a SECURITY
|
||||
// INVOKER trigger writing to a policy-less RLS table), not a user-permission
|
||||
// problem. Kept distinct from FORBIDDEN (which blames the user) and from
|
||||
// INTERNAL_ERROR (which hides the failure mode from diagnostics).
|
||||
DB_PERMISSION_DENIED: {
|
||||
httpStatus: 500,
|
||||
message_sv: 'Ett behörighetsfel i databasen stoppade åtgärden. Kontakta supporten om felet kvarstår.',
|
||||
message_en: 'A database permission (RLS) denial blocked the write. This indicates a server-side misconfiguration.',
|
||||
},
|
||||
NOT_FOUND: {
|
||||
httpStatus: 404,
|
||||
message_sv: 'Resursen kunde inte hittas.',
|
||||
|
||||
Reference in New Issue
Block a user