Prevent sharing a diagram with yourself

RLS is the real guard (diagram_collaborators_insert/update now reject
user_id = the diagram's owner, regardless of who's performing the write —
covers a Super Admin acting on someone else's diagram too, not just the
normal owner path); the client-side check in
SupabaseDiagramCollaboratorRepository.add is just there to surface a
friendly message instead of the raw 42501.

Verified: tsc -b and oxlint clean; supabase db reset + 53/53 pgTAP tests
pass (1 new test).
This commit is contained in:
2026-09-11 11:42:07 -05:00
parent dea26f7ee8
commit 4b757e89f5
5 changed files with 55 additions and 4 deletions
@@ -0,0 +1,29 @@
-- Prevents a diagram's owner from ending up as their own collaborator row
-- (found via manual testing: the UI let you "share" a diagram with
-- yourself). Ownership already implies full access, so a self-collaborator
-- row is never meaningful — enforced here, not just in the client, since
-- the client-side check alone wouldn't stop a Super Admin's "add on behalf
-- of the owner" path or any other direct API access from creating one.
drop policy "diagram_collaborators_insert" on public.diagram_collaborators;
create policy "diagram_collaborators_insert" on public.diagram_collaborators for insert
with check (
(public.is_super_admin() or public.diagram_owner_id(diagram_id) = auth.uid())
and user_id != public.diagram_owner_id(diagram_id)
);
-- The existing update policy had no WITH CHECK at all (only USING) — added
-- here too, defensively, in case user_id (part of the primary key) is ever
-- changed via UPDATE rather than delete+insert.
drop policy "diagram_collaborators_update" on public.diagram_collaborators;
create policy "diagram_collaborators_update" on public.diagram_collaborators for update
using (
public.is_super_admin()
or public.diagram_owner_id(diagram_id) = auth.uid()
)
with check (
(public.is_super_admin() or public.diagram_owner_id(diagram_id) = auth.uid())
and user_id != public.diagram_owner_id(diagram_id)
);
+8 -1
View File
@@ -25,7 +25,7 @@ begin;
create extension if not exists pgtap with schema extensions;
select plan(52);
select plan(53);
-- ----------------------------------------------------------------------
-- Fixtures (as postgres — RLS does not apply)
@@ -138,6 +138,13 @@ select throws_ok(
select set_config('request.jwt.claim.sub', '11111111-1111-1111-1111-111111111111', true);
select throws_ok(
$$ insert into public.diagram_collaborators (diagram_id, user_id, permission)
values ('b0000000-0000-0000-0000-000000000001', '11111111-1111-1111-1111-111111111111', 'edit') $$,
'42501'::char(5), null,
'alice (owner) cannot add herself as a collaborator on her own diagram'
);
select lives_ok(
$$ insert into public.diagram_collaborators (diagram_id, user_id, permission)
values ('b0000000-0000-0000-0000-000000000001', '22222222-2222-2222-2222-222222222222', 'view') $$,