CREATE OR REPLACE FUNCTION public.current_profile_id() RETURNS uuid LANGUAGE sql STABLE AS $$ SELECT nullif(current_setting('request.jwt.claim.sub', true), '')::uuid; $$; CREATE OR REPLACE FUNCTION public.is_tenant_member(p_tenant_id uuid) RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT EXISTS ( SELECT 1 FROM public.memberships WHERE tenant_id = p_tenant_id AND profile_id = public.current_profile_id() ); $$; CREATE OR REPLACE FUNCTION public.has_tenant_role(p_tenant_id uuid, p_roles public.membership_role[]) RETURNS boolean LANGUAGE sql STABLE SECURITY DEFINER SET search_path = public AS $$ SELECT EXISTS ( SELECT 1 FROM public.memberships WHERE tenant_id = p_tenant_id AND profile_id = public.current_profile_id() AND role = ANY (p_roles) ); $$; ALTER TABLE public.tenants ENABLE ROW LEVEL SECURITY; ALTER TABLE public.memberships ENABLE ROW LEVEL SECURITY; ALTER TABLE public.projects ENABLE ROW LEVEL SECURITY; ALTER TABLE public.project_quotas ENABLE ROW LEVEL SECURITY; ALTER TABLE public.vms ENABLE ROW LEVEL SECURITY; ALTER TABLE public.ssh_keys ENABLE ROW LEVEL SECURITY; ALTER TABLE public.audit_log ENABLE ROW LEVEL SECURITY; CREATE POLICY tenants_member_select ON public.tenants FOR SELECT USING (public.is_tenant_member(id)); CREATE POLICY memberships_member_select ON public.memberships FOR SELECT USING (public.is_tenant_member(tenant_id)); CREATE POLICY projects_member_select ON public.projects FOR SELECT USING (public.is_tenant_member(tenant_id)); CREATE POLICY project_quotas_member_select ON public.project_quotas FOR SELECT USING ( EXISTS ( SELECT 1 FROM public.projects WHERE projects.id = project_quotas.project_id AND public.is_tenant_member(projects.tenant_id) ) ); CREATE POLICY vms_member_select ON public.vms FOR SELECT USING ( EXISTS ( SELECT 1 FROM public.projects WHERE projects.id = vms.project_id AND public.is_tenant_member(projects.tenant_id) ) ); CREATE POLICY ssh_keys_member_select ON public.ssh_keys FOR SELECT USING (public.is_tenant_member(tenant_id)); CREATE POLICY audit_log_admin_select ON public.audit_log FOR SELECT USING (public.has_tenant_role(tenant_id, ARRAY['owner', 'admin']::public.membership_role[]));