Initial commit from v0
This commit is contained in:
@@ -0,0 +1,516 @@
|
||||
-- =====================================================
|
||||
-- COMPREHENSIVE RLS FIX
|
||||
-- Fixes infinite recursion + enum cast issues
|
||||
-- =====================================================
|
||||
|
||||
-- Helper: returns the space_ids the user belongs to (security definer avoids recursion)
|
||||
create or replace function public.get_user_space_ids(uid uuid)
|
||||
returns setof uuid
|
||||
language sql
|
||||
security definer
|
||||
stable
|
||||
set search_path = public
|
||||
as $$
|
||||
select space_id from public.space_members where user_id = uid
|
||||
$$;
|
||||
|
||||
-- Helper: checks if user has one of the given roles in a space
|
||||
-- NOTE: role column is a member_role enum, so we cast to text for comparison
|
||||
create or replace function public.user_has_role_in_space(uid uuid, sid uuid, allowed_roles text[])
|
||||
returns boolean
|
||||
language sql
|
||||
security definer
|
||||
stable
|
||||
set search_path = public
|
||||
as $$
|
||||
select exists (
|
||||
select 1 from public.space_members
|
||||
where user_id = uid
|
||||
and space_id = sid
|
||||
and role::text = any(allowed_roles)
|
||||
)
|
||||
$$;
|
||||
|
||||
-- Grant execute permissions
|
||||
grant execute on function public.get_user_space_ids(uuid) to authenticated, service_role;
|
||||
grant execute on function public.user_has_role_in_space(uuid, uuid, text[]) to authenticated, service_role;
|
||||
|
||||
-- =====================================================
|
||||
-- SPACES
|
||||
-- =====================================================
|
||||
drop policy if exists "spaces_select_members" on public.spaces;
|
||||
drop policy if exists "spaces_insert_authenticated" on public.spaces;
|
||||
drop policy if exists "spaces_update_admins" on public.spaces;
|
||||
drop policy if exists "spaces_delete_admins" on public.spaces;
|
||||
|
||||
create policy "spaces_select_members" on public.spaces
|
||||
for select using (id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "spaces_insert_authenticated" on public.spaces
|
||||
for insert with check (auth.uid() is not null);
|
||||
|
||||
create policy "spaces_update_admins" on public.spaces
|
||||
for update using (public.user_has_role_in_space(auth.uid(), id, array['admin']));
|
||||
|
||||
create policy "spaces_delete_admins" on public.spaces
|
||||
for delete using (public.user_has_role_in_space(auth.uid(), id, array['admin']));
|
||||
|
||||
-- =====================================================
|
||||
-- SPACE MEMBERS
|
||||
-- =====================================================
|
||||
drop policy if exists "space_members_select_own" on public.space_members;
|
||||
drop policy if exists "space_members_select_same_space" on public.space_members;
|
||||
drop policy if exists "space_members_select_own_space" on public.space_members;
|
||||
drop policy if exists "space_members_insert_authenticated" on public.space_members;
|
||||
drop policy if exists "space_members_update_admins" on public.space_members;
|
||||
drop policy if exists "space_members_delete_admins" on public.space_members;
|
||||
|
||||
create policy "space_members_select_own" on public.space_members
|
||||
for select using (user_id = auth.uid());
|
||||
|
||||
create policy "space_members_select_same_space" on public.space_members
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
-- Any authenticated user can insert a row where user_id = themselves
|
||||
create policy "space_members_insert_authenticated" on public.space_members
|
||||
for insert with check (auth.uid() is not null and user_id = auth.uid());
|
||||
|
||||
-- Users can update their own profile; admins can update anyone in their space
|
||||
create policy "space_members_update_admins" on public.space_members
|
||||
for update using (
|
||||
user_id = auth.uid()
|
||||
or public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
create policy "space_members_delete_admins" on public.space_members
|
||||
for delete using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- TASKS
|
||||
-- =====================================================
|
||||
drop policy if exists "tasks_select_members" on public.tasks;
|
||||
drop policy if exists "tasks_insert_members" on public.tasks;
|
||||
drop policy if exists "tasks_update_members" on public.tasks;
|
||||
drop policy if exists "tasks_delete_members" on public.tasks;
|
||||
|
||||
create policy "tasks_select_members" on public.tasks
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "tasks_insert_members" on public.tasks
|
||||
for insert with check (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "tasks_update_members" on public.tasks
|
||||
for update using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "tasks_delete_members" on public.tasks
|
||||
for delete using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
-- =====================================================
|
||||
-- PROJECTS
|
||||
-- =====================================================
|
||||
drop policy if exists "projects_select_members" on public.projects;
|
||||
drop policy if exists "projects_insert_members" on public.projects;
|
||||
drop policy if exists "projects_update_members" on public.projects;
|
||||
drop policy if exists "projects_delete_members" on public.projects;
|
||||
|
||||
create policy "projects_select_members" on public.projects
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "projects_insert_members" on public.projects
|
||||
for insert with check (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "projects_update_members" on public.projects
|
||||
for update using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "projects_delete_members" on public.projects
|
||||
for delete using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
-- =====================================================
|
||||
-- KNOWLEDGE BASE
|
||||
-- =====================================================
|
||||
drop policy if exists "kb_select_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_insert_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_update_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_delete_board" on public.knowledge_base;
|
||||
|
||||
create policy "kb_select_members" on public.knowledge_base
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "kb_insert_members" on public.knowledge_base
|
||||
for insert with check (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "kb_update_members" on public.knowledge_base
|
||||
for update using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "kb_delete_board" on public.knowledge_base
|
||||
for delete using (public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board']));
|
||||
|
||||
-- =====================================================
|
||||
-- SECRETS
|
||||
-- =====================================================
|
||||
drop policy if exists "secrets_select_admins" on public.secrets;
|
||||
drop policy if exists "secrets_modify_admins" on public.secrets;
|
||||
|
||||
create policy "secrets_select_admins" on public.secrets
|
||||
for select using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board', 'treasurer'])
|
||||
);
|
||||
|
||||
create policy "secrets_modify_admins" on public.secrets
|
||||
for all using (public.user_has_role_in_space(auth.uid(), space_id, array['admin']));
|
||||
|
||||
-- =====================================================
|
||||
-- AREA LEADS
|
||||
-- =====================================================
|
||||
drop policy if exists "area_leads_select_members" on public.area_leads;
|
||||
drop policy if exists "area_leads_modify_board" on public.area_leads;
|
||||
|
||||
create policy "area_leads_select_members" on public.area_leads
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "area_leads_modify_board" on public.area_leads
|
||||
for all using (public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board']));
|
||||
|
||||
-- =====================================================
|
||||
-- CONTACTS
|
||||
-- =====================================================
|
||||
drop policy if exists "contacts_select_members" on public.contacts;
|
||||
drop policy if exists "contacts_modify_members" on public.contacts;
|
||||
|
||||
create policy "contacts_select_members" on public.contacts
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "contacts_modify_members" on public.contacts
|
||||
for all using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
-- =====================================================
|
||||
-- PAYMENTS
|
||||
-- =====================================================
|
||||
drop policy if exists "payments_select_treasurer" on public.payments;
|
||||
drop policy if exists "payments_modify_treasurer" on public.payments;
|
||||
|
||||
create policy "payments_select_treasurer" on public.payments
|
||||
for select using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board', 'treasurer'])
|
||||
);
|
||||
|
||||
create policy "payments_modify_treasurer" on public.payments
|
||||
for all using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'treasurer'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- COMMS CHANNELS
|
||||
-- =====================================================
|
||||
drop policy if exists "channels_select_members" on public.comms_channels;
|
||||
drop policy if exists "channels_insert_board" on public.comms_channels;
|
||||
drop policy if exists "channels_update_board" on public.comms_channels;
|
||||
drop policy if exists "channels_delete_board" on public.comms_channels;
|
||||
|
||||
create policy "channels_select_members" on public.comms_channels
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "channels_insert_board" on public.comms_channels
|
||||
for insert with check (public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board']));
|
||||
|
||||
create policy "channels_update_board" on public.comms_channels
|
||||
for update using (public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board']));
|
||||
|
||||
create policy "channels_delete_board" on public.comms_channels
|
||||
for delete using (public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board']));
|
||||
|
||||
-- =====================================================
|
||||
-- COMMS MESSAGES
|
||||
-- =====================================================
|
||||
drop policy if exists "messages_select_members" on public.comms_messages;
|
||||
drop policy if exists "messages_insert_members" on public.comms_messages;
|
||||
|
||||
create policy "messages_select_members" on public.comms_messages
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "messages_insert_members" on public.comms_messages
|
||||
for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
and user_id = auth.uid()
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- INTEGRATIONS
|
||||
-- =====================================================
|
||||
drop policy if exists "integrations_select_admins" on public.integrations;
|
||||
drop policy if exists "integrations_modify_admins" on public.integrations;
|
||||
|
||||
create policy "integrations_select_admins" on public.integrations
|
||||
for select using (public.user_has_role_in_space(auth.uid(), space_id, array['admin']));
|
||||
|
||||
create policy "integrations_modify_admins" on public.integrations
|
||||
for all using (public.user_has_role_in_space(auth.uid(), space_id, array['admin']));
|
||||
|
||||
-- =====================================================
|
||||
-- ACTIVITY LOG
|
||||
-- =====================================================
|
||||
drop policy if exists "activity_select_members" on public.activity_log;
|
||||
drop policy if exists "activity_insert_members" on public.activity_log;
|
||||
|
||||
create policy "activity_select_members" on public.activity_log
|
||||
for select using (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
create policy "activity_insert_members" on public.activity_log
|
||||
for insert with check (space_id in (select public.get_user_space_ids(auth.uid())));
|
||||
|
||||
|
||||
-- =====================================================
|
||||
-- SPACES
|
||||
-- =====================================================
|
||||
drop policy if exists "spaces_select_members" on public.spaces;
|
||||
drop policy if exists "spaces_insert_authenticated" on public.spaces;
|
||||
drop policy if exists "spaces_update_admins" on public.spaces;
|
||||
|
||||
create policy "spaces_select_members" on public.spaces for select using (
|
||||
id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "spaces_insert_authenticated" on public.spaces for insert
|
||||
with check (auth.uid() is not null);
|
||||
|
||||
create policy "spaces_update_admins" on public.spaces for update using (
|
||||
public.user_has_role_in_space(auth.uid(), id, array['admin'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- SPACE MEMBERS
|
||||
-- =====================================================
|
||||
drop policy if exists "space_members_select_own" on public.space_members;
|
||||
drop policy if exists "space_members_select_same_space" on public.space_members;
|
||||
drop policy if exists "space_members_select_own_space" on public.space_members;
|
||||
drop policy if exists "space_members_insert_authenticated" on public.space_members;
|
||||
drop policy if exists "space_members_update_admins" on public.space_members;
|
||||
drop policy if exists "space_members_delete_admins" on public.space_members;
|
||||
|
||||
-- SELECT: Users can see their own record
|
||||
create policy "space_members_select_own" on public.space_members for select using (
|
||||
user_id = auth.uid()
|
||||
);
|
||||
|
||||
-- SELECT: Users can see all members in spaces they belong to
|
||||
create policy "space_members_select_same_space" on public.space_members for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
-- INSERT: Authenticated users can add themselves to a space
|
||||
create policy "space_members_insert_authenticated" on public.space_members for insert
|
||||
with check (auth.uid() is not null and user_id = auth.uid());
|
||||
|
||||
-- UPDATE: Admins can update any member, users can update themselves
|
||||
create policy "space_members_update_admins" on public.space_members for update using (
|
||||
user_id = auth.uid()
|
||||
or public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
-- DELETE: Only admins can remove members
|
||||
create policy "space_members_delete_admins" on public.space_members for delete using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- TASKS
|
||||
-- =====================================================
|
||||
drop policy if exists "tasks_select_members" on public.tasks;
|
||||
drop policy if exists "tasks_insert_members" on public.tasks;
|
||||
drop policy if exists "tasks_update_members" on public.tasks;
|
||||
drop policy if exists "tasks_delete_members" on public.tasks;
|
||||
|
||||
create policy "tasks_select_members" on public.tasks for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "tasks_insert_members" on public.tasks for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "tasks_update_members" on public.tasks for update using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "tasks_delete_members" on public.tasks for delete using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- PROJECTS
|
||||
-- =====================================================
|
||||
drop policy if exists "projects_select_members" on public.projects;
|
||||
drop policy if exists "projects_insert_members" on public.projects;
|
||||
drop policy if exists "projects_update_members" on public.projects;
|
||||
drop policy if exists "projects_delete_members" on public.projects;
|
||||
|
||||
create policy "projects_select_members" on public.projects for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "projects_insert_members" on public.projects for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "projects_update_members" on public.projects for update using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "projects_delete_members" on public.projects for delete using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- KNOWLEDGE BASE
|
||||
-- =====================================================
|
||||
drop policy if exists "kb_select_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_insert_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_update_members" on public.knowledge_base;
|
||||
drop policy if exists "kb_delete_board" on public.knowledge_base;
|
||||
|
||||
create policy "kb_select_members" on public.knowledge_base for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "kb_insert_members" on public.knowledge_base for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "kb_update_members" on public.knowledge_base for update using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "kb_delete_board" on public.knowledge_base for delete using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- SECRETS
|
||||
-- =====================================================
|
||||
drop policy if exists "secrets_select_admins" on public.secrets;
|
||||
drop policy if exists "secrets_modify_admins" on public.secrets;
|
||||
|
||||
create policy "secrets_select_admins" on public.secrets for select using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board', 'treasurer'])
|
||||
);
|
||||
|
||||
create policy "secrets_modify_admins" on public.secrets for all using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- AREA LEADS
|
||||
-- =====================================================
|
||||
drop policy if exists "area_leads_select_members" on public.area_leads;
|
||||
drop policy if exists "area_leads_modify_board" on public.area_leads;
|
||||
|
||||
create policy "area_leads_select_members" on public.area_leads for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "area_leads_modify_board" on public.area_leads for all using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- CONTACTS
|
||||
-- =====================================================
|
||||
drop policy if exists "contacts_select_members" on public.contacts;
|
||||
drop policy if exists "contacts_modify_members" on public.contacts;
|
||||
|
||||
create policy "contacts_select_members" on public.contacts for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "contacts_modify_members" on public.contacts for all using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- PAYMENTS
|
||||
-- =====================================================
|
||||
drop policy if exists "payments_select_treasurer" on public.payments;
|
||||
drop policy if exists "payments_modify_treasurer" on public.payments;
|
||||
|
||||
create policy "payments_select_treasurer" on public.payments for select using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board', 'treasurer'])
|
||||
);
|
||||
|
||||
create policy "payments_modify_treasurer" on public.payments for all using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'treasurer'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- COMMS CHANNELS
|
||||
-- =====================================================
|
||||
drop policy if exists "channels_select_members" on public.comms_channels;
|
||||
drop policy if exists "channels_insert_board" on public.comms_channels;
|
||||
drop policy if exists "channels_update_board" on public.comms_channels;
|
||||
drop policy if exists "channels_delete_board" on public.comms_channels;
|
||||
|
||||
create policy "channels_select_members" on public.comms_channels for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "channels_insert_board" on public.comms_channels for insert with check (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board'])
|
||||
);
|
||||
|
||||
create policy "channels_update_board" on public.comms_channels for update using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board'])
|
||||
);
|
||||
|
||||
create policy "channels_delete_board" on public.comms_channels for delete using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin', 'board'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- COMMS MESSAGES
|
||||
-- =====================================================
|
||||
drop policy if exists "messages_select_members" on public.comms_messages;
|
||||
drop policy if exists "messages_insert_members" on public.comms_messages;
|
||||
|
||||
create policy "messages_select_members" on public.comms_messages for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "messages_insert_members" on public.comms_messages for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
and user_id = auth.uid()
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- INTEGRATIONS
|
||||
-- =====================================================
|
||||
drop policy if exists "integrations_select_admins" on public.integrations;
|
||||
drop policy if exists "integrations_modify_admins" on public.integrations;
|
||||
|
||||
create policy "integrations_select_admins" on public.integrations for select using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
create policy "integrations_modify_admins" on public.integrations for all using (
|
||||
public.user_has_role_in_space(auth.uid(), space_id, array['admin'])
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- ACTIVITY LOG
|
||||
-- =====================================================
|
||||
drop policy if exists "activity_select_members" on public.activity_log;
|
||||
drop policy if exists "activity_insert_members" on public.activity_log;
|
||||
|
||||
create policy "activity_select_members" on public.activity_log for select using (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
create policy "activity_insert_members" on public.activity_log for insert with check (
|
||||
space_id in (select public.get_user_space_ids(auth.uid()))
|
||||
);
|
||||
|
||||
-- =====================================================
|
||||
-- GRANT EXECUTE on helper functions to authenticated users
|
||||
-- =====================================================
|
||||
grant execute on function public.get_user_space_ids(uuid) to authenticated;
|
||||
grant execute on function public.user_has_role_in_space(uuid, uuid, text[]) to authenticated;
|
||||
Reference in New Issue
Block a user