fix(rls): status-gate user_has_role_in_space + user_has_permission (046)

Lifts the deliberately-deferred SECURITY DEFINER status-gate. The
deferral's blocking reason ('no way to verify it breaks nothing') is
resolved by the integration harness, so the gap is now closed at the
RLS layer too (defense-in-depth beyond D2's app layer): an unverified/
inactive member can no longer exercise a role or permission via direct
PostgREST. Scoped to the two privilege entrypoints; get_user_space_ids
+ user_effective_roles deliberately untouched so unverified self-reads/
onboarding still work. Migration 046 + schema.sql mirror. New
integration test (5) proves both halves: gap closed AND
current/late/approval/unverified-reads all unbroken. 31 integration
tests green; default pnpm test 532/26 hermetic; build clean.
This commit is contained in:
Moheeb Zara
2026-05-19 14:04:31 -07:00
parent ee7f59f1f4
commit e954b08232
3 changed files with 160 additions and 6 deletions
+14 -6
View File
@@ -385,6 +385,7 @@ AS $$
WHERE user_id = uid
AND space_id = sid
AND role::text = ANY(allowed_roles)
AND status IN ('current','late') -- privilege-eligible only (046)
);
$$;
@@ -1753,12 +1754,19 @@ $$;
CREATE OR REPLACE FUNCTION public.user_has_permission(uid uuid, sid uuid, perm text)
RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public
AS $$
SELECT public.user_has_role_in_space(uid, sid, ARRAY['admin'])
OR EXISTS (
SELECT 1 FROM public.space_role_permissions p
WHERE p.space_id = sid AND p.permission = perm
AND p.subject IN (SELECT public.user_effective_roles(uid, sid))
);
SELECT EXISTS (
SELECT 1 FROM public.space_members
WHERE user_id = uid AND space_id = sid
AND status IN ('current','late') -- no permission unless privilege-eligible (046)
)
AND (
public.user_has_role_in_space(uid, sid, ARRAY['admin'])
OR EXISTS (
SELECT 1 FROM public.space_role_permissions p
WHERE p.space_id = sid AND p.permission = perm
AND p.subject IN (SELECT public.user_effective_roles(uid, sid))
)
);
$$;
ALTER TABLE public.space_role_permissions ENABLE ROW LEVEL SECURITY;