Organisationslagret: multi-tenant med roller, integrationstestat

Master Prompts tenant- och rollmodell implementerad i den självhostade
plattformen:

- Registrering skapar organisation + systemadministratör (transaktion);
  admin skapar övriga användare (tekniker/arbetsledare/admin) i sin
  organisation via nytt UI och POST /api/anvandare. Roll + organisation
  ligger i JWT-anspråken och verifieras alltid på servern.
- All ärendedata organisationsknuten: ärenden skapas i användarens
  organisation, händelse-API:t verifierar tillhörighet per anrop och
  GET /api/arenden listar bara egna organisationens ärenden.
- Schemat flyttat till infra/k8s/postgres-init.sql och genereras in som
  ConfigMap via kustomize — samma fil används av integrationstestet.
  Nya tabeller: organisationer; anvandare med organisation_id + roll
  (check-constraint); arenden med organisation_id + index.
- Integrationstest (services/plattform/integrationstest.sh) mot riktig
  Postgres, kört grönt lokalt och tillagt i CI: registrering, synk,
  idempotens (händelser skrivs aldrig över), append-only-triggern,
  organisationsisolering (404 över gränsen), publik delning med
  filtrering, rollstyrning (tekniker nekas användarhantering) och
  delad arbetsyta inom organisationen.
- Klienten: registreringsformulär med organisationsfält, kontopanel
  med roll och organisation, admin-panel för användarhantering.

Verifierat: integrationstestets 12 kontroller gröna mot Postgres 16,
28 vitest-tester gröna, produktionsbygge ok, YAML validerad,
Playwright-röktest grönt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
This commit is contained in:
Claude
2026-08-03 07:04:32 +00:00
parent 7c24186cc5
commit b069c9a12f
11 changed files with 553 additions and 164 deletions
+6
View File
@@ -9,3 +9,9 @@ resources:
- ingress.yaml
# hemligheter.exempel.yaml appliceras medvetet inte — skapa secret:en
# separat (se kommentaren i den filen).
configMapGenerator:
- name: postgres-init
namespace: guidad-felsokning
files:
- init.sql=postgres-init.sql
+64
View File
@@ -0,0 +1,64 @@
-- Guidad Felsökning: schema för den självhostade plattformen.
-- Multi-tenant: varje organisation är en egen tenant — all ärendedata är
-- organisationsknuten och API:t släpper aldrig data över organisationsgränsen.
-- Append-only garanteras med triggers: historik kan inte ändras eller
-- raderas oavsett vilken roll som ansluter.
create extension if not exists pgcrypto;
create table if not exists organisationer (
id uuid primary key default gen_random_uuid(),
namn text not null,
skapad timestamptz not null default now()
);
create table if not exists anvandare (
id uuid primary key default gen_random_uuid(),
organisation_id uuid not null references organisationer(id),
epost text unique not null,
losen_hash text not null,
namn text not null,
roll text not null default 'tekniker'
check (roll in ('tekniker', 'arbetsledare', 'admin')),
skapad timestamptz not null default now()
);
create table if not exists felsokning_arenden (
id text primary key,
organisation_id uuid not null references organisationer(id),
nummer integer not null,
skapad timestamptz not null,
delningskod text unique,
metodik_id text,
skapad_av uuid references anvandare(id),
insatt timestamptz not null default now()
);
create index if not exists felsokning_arenden_org_idx
on felsokning_arenden (organisation_id, skapad desc);
create table if not exists felsokning_handelser (
id text primary key,
arende_id text not null references felsokning_arenden(id),
tidpunkt timestamptz not null,
anvandare text not null,
handelse jsonb not null,
insatt timestamptz not null default now()
);
create index if not exists felsokning_handelser_arende_idx
on felsokning_handelser (arende_id, tidpunkt);
create or replace function forbjud_andring() returns trigger
language plpgsql as $$
begin
raise exception 'Loggen är append-only — historik kan inte ändras eller raderas';
end $$;
drop trigger if exists handelser_append_only on felsokning_handelser;
create trigger handelser_append_only
before update or delete on felsokning_handelser
for each row execute function forbjud_andring();
drop trigger if exists arenden_append_only on felsokning_arenden;
create trigger arenden_append_only
before update or delete on felsokning_arenden
for each row execute function forbjud_andring();
+4 -60
View File
@@ -1,64 +1,8 @@
# Självhostad Postgres för Guidad Felsökning.
# För produktion rekommenderas CloudNativePG-operatorn (automatiska
# säkerhetskopior, failover, PITR) — den här StatefulSet:en är den enkla
# grundplattan med samma schema.
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-init
namespace: guidad-felsokning
data:
# Körs vid första starten. Append-only garanteras med triggers —
# historiken kan inte ändras eller raderas oavsett vilken roll som
# ansluter.
init.sql: |
create extension if not exists pgcrypto;
create table if not exists anvandare (
id uuid primary key default gen_random_uuid(),
epost text unique not null,
losen_hash text not null,
namn text not null,
skapad timestamptz not null default now()
);
create table if not exists felsokning_arenden (
id text primary key,
nummer integer not null,
skapad timestamptz not null,
delningskod text unique,
metodik_id text,
skapad_av uuid references anvandare(id),
insatt timestamptz not null default now()
);
create table if not exists felsokning_handelser (
id text primary key,
arende_id text not null references felsokning_arenden(id),
tidpunkt timestamptz not null,
anvandare text not null,
handelse jsonb not null,
insatt timestamptz not null default now()
);
create index if not exists felsokning_handelser_arende_idx
on felsokning_handelser (arende_id, tidpunkt);
create or replace function forbjud_andring() returns trigger
language plpgsql as $$
begin
raise exception 'Loggen är append-only — historik kan inte ändras eller raderas';
end $$;
drop trigger if exists handelser_append_only on felsokning_handelser;
create trigger handelser_append_only
before update or delete on felsokning_handelser
for each row execute function forbjud_andring();
drop trigger if exists arenden_append_only on felsokning_arenden;
create trigger arenden_append_only
before update or delete on felsokning_arenden
for each row execute function forbjud_andring();
---
# Schemat (infra/k8s/postgres-init.sql) genereras in som ConfigMap av
# kustomize och körs vid första starten — samma fil används av
# integrationstestet. För produktion rekommenderas CloudNativePG-operatorn
# (automatiska säkerhetskopior, failover, PITR).
apiVersion: apps/v1
kind: StatefulSet
metadata: