diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index b98647a..e4be0a8 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -67,11 +67,7 @@ } // Sort by created_at descending - nodes.sort((a, b) => { - const ta = a.createdAt?.microsSinceUnixEpoch ?? 0n; - const tb = b.createdAt?.microsSinceUnixEpoch ?? 0n; - return tb > ta ? 1 : tb < ta ? -1 : 0; - }); + nodes.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0)); return nodes; }); diff --git a/maskinrommet/src/intentions.rs b/maskinrommet/src/intentions.rs index fa63ded..642eafb 100644 --- a/maskinrommet/src/intentions.rs +++ b/maskinrommet/src/intentions.rs @@ -595,6 +595,16 @@ pub async fn create_node( "belongs_to-edge opprettet (kontekst-arv)" ); + // Propager tilgang: alle som har tilgang til ctx_id får tilgang til node_id + if let Err(e) = sqlx::query("SELECT propagate_belongs_to_access($1, $2)") + .bind(node_id) + .bind(ctx_id) + .execute(&state.db) + .await + { + tracing::error!("propagate_belongs_to_access feilet: {e}"); + } + Some(edge_id) } else { None @@ -881,6 +891,18 @@ pub async fn create_edge( internal_error("Databasefeil ved opprettelse av edge") })?; + // Propager tilgang ved belongs_to: alle som har tilgang til target (forelder) får tilgang til source (barn) + if req.edge_type == "belongs_to" { + if let Err(e) = sqlx::query("SELECT propagate_belongs_to_access($1, $2)") + .bind(req.source_id) + .bind(req.target_id) + .execute(&state.db) + .await + { + tracing::error!("propagate_belongs_to_access feilet: {e}"); + } + } + // Trigger rendering ved belongs_to if req.edge_type == "belongs_to" { if let Ok(Some(config)) = crate::publishing::find_publishing_collection_by_id(&state.db, req.target_id).await { @@ -1946,6 +1968,16 @@ pub async fn create_communication( context_id = %context_id, "belongs_to-edge opprettet til kontekstnode" ); + + // Propager tilgang fra kontekstnoden + if let Err(e) = sqlx::query("SELECT propagate_belongs_to_access($1, $2)") + .bind(node_id) + .bind(context_id) + .execute(&state.db) + .await + { + tracing::error!("propagate_belongs_to_access feilet: {e}"); + } } tracing::info!( diff --git a/migrations/020_belongs_to_access_propagation.sql b/migrations/020_belongs_to_access_propagation.sql new file mode 100644 index 0000000..1f43112 --- /dev/null +++ b/migrations/020_belongs_to_access_propagation.sql @@ -0,0 +1,51 @@ +-- Migrasjon 020: Propager tilgang ved nye belongs_to-edges +-- +-- Når en belongs_to-edge opprettes mellom barn og forelder, skal alle +-- som allerede har tilgang til forelderen også få tilgang til barnet. +-- recompute_access håndterer dette i steg 2 for access-granting edges, +-- men ikke for belongs_to-edges som opprettes etter at tilgang allerede er gitt. + +CREATE OR REPLACE FUNCTION propagate_belongs_to_access( + p_child_id UUID, + p_parent_id UUID +) RETURNS void LANGUAGE plpgsql AS $$ +BEGIN + -- For alle som har tilgang til forelder: gi samme tilgang til barnet + INSERT INTO node_access (subject_id, object_id, access, via_edge) + SELECT na.subject_id, p_child_id, na.access, na.via_edge + FROM node_access na + WHERE na.object_id = p_parent_id + ON CONFLICT (subject_id, object_id) + DO UPDATE SET access = GREATEST(node_access.access, EXCLUDED.access), + via_edge = CASE + WHEN EXCLUDED.access > node_access.access THEN EXCLUDED.via_edge + ELSE node_access.via_edge + END; +END; +$$; + +-- Retroaktivt: fiks eksisterende belongs_to-edges der tilgang mangler. +-- Finn barn som har belongs_to-edge til forelder, men der forelderens +-- tilgangssubjekter ikke har tilgang til barnet. +DO $$ +DECLARE + r RECORD; +BEGIN + FOR r IN + SELECT e.source_id AS child_id, e.target_id AS parent_id + FROM edges e + WHERE e.edge_type = 'belongs_to' + AND EXISTS ( + SELECT 1 FROM node_access na + WHERE na.object_id = e.target_id + AND NOT EXISTS ( + SELECT 1 FROM node_access na2 + WHERE na2.subject_id = na.subject_id + AND na2.object_id = e.source_id + ) + ) + LOOP + PERFORM propagate_belongs_to_access(r.child_id, r.parent_id); + END LOOP; +END; +$$; diff --git a/tasks.md b/tasks.md index bc43eac..6b8cb01 100644 --- a/tasks.md +++ b/tasks.md @@ -296,8 +296,7 @@ fiks direkte hvis det er småting, eller opprett nye work_items (tasks) med spesifikasjon for det som trenger en dedikert sesjon. - [x] 23.1 Valider fase 1–2 (infra + maskinrommet): PG-skjema, indekser, auth-middleware, intensjoner, STDB-klient (nå erstattet av WS). Verifiser at skjema matcher docs, at auth fungerer, at skrivestien er konsistent. -- [~] 23.2 Valider fase 3–4 (frontend + tilgang): SvelteKit-oppsett, OIDC-flow, sanntid, mottaksflate, TipTap-editor, node_access-matrise, team-transitivitet, visibility-filtrering. - > Påbegynt: 2026-03-18T14:30 +- [x] 23.2 Valider fase 3–4 (frontend + tilgang): SvelteKit-oppsett, OIDC-flow, sanntid, mottaksflate, TipTap-editor, node_access-matrise, team-transitivitet, visibility-filtrering. - [ ] 23.3 Valider fase 5–8 (kommunikasjon + CAS + lyd + aliaser): chat-loop, kontekst-arv, CAS-hashing/deduplisering, Whisper-pipeline, segmenttabell, SRT-eksport, alias-identitet. - [ ] 23.4 Valider fase 9–10 (visninger + AI): kanban drag-and-drop, kalender, dagbok, kunnskapsgraf, LiteLLM-ruting, AI-foreslåtte edges, oppsummering, TTS. - [ ] 23.5 Valider fase 11 (produksjon): LiveKit-oppsett, sanntidslyd, pruning-logikk, podcast-RSS.