postgresqlsupabase

Insert with roles


i've implemented an insert policy checking auth_role with a function, this worked with other projects that i've made but since yesterday if i do a request inserting another row into the table question_types, returns 403 error.

Fun facts:

    SELECT 1
    FROM user_roles ur
    JOIN roles r ON ur.role_id = r.id
    WHERE ur.user_id = <USER_ID> AND r.name = 'advisor'
 
create table public.question_types (
  id integer primary key generated always as identity,
  question_id integer references public.questions(id) on delete cascade,
  evaluation_type_id integer references public.evaluation_types(id) on delete cascade,
  created_at timestamp with time zone default now(),
  updated_at timestamp with time zone default now()
);

CREATE OR REPLACE FUNCTION auth_role(role_name text)
RETURNS boolean LANGUAGE plpgsql AS $$
BEGIN
  RETURN EXISTS(
    SELECT 1
    FROM user_roles ur
    JOIN roles r ON ur.role_id = r.id
    WHERE ur.user_id = auth.uid() AND r.name = role_name
  );
END;
$$;

alter table public.question_types
 enable row level security;


CREATE POLICY "Insert can be done by authenticated users." ON public.question_types
FOR INSERT TO authenticated
WITH CHECK (auth_role('superadmin'));

CREATE POLICY "Insert can be done by authenticated users advisor." ON public.question_types
FOR INSERT TO authenticated 
WITH CHECK (auth_role('advisor'));

Solution

  • i have missed to put a select policy to authenticated users on roles table.

    CREATE POLICY "Only authenticated can view the roles." 
    ON public.roles
    FOR SELECT TO authenticated
    USING(true);
    

    The bug was related to the last RLS policy created:

    CREATE POLICY "Only authenticated can view the roles." 
    ON public.roles
    FOR SELECT TO authenticated
    USING(auth_role('superadmin'));
    

    So i guess that when the user was trying to insert a new row question_types the last flow checked the auth_role before insert, checking roles table but, the RLS from that table did not allow to select any row from roles table if the user wasn't superadmin

     SELECT 1
        FROM user_roles ur
        JOIN roles r ON ur.role_id = r.id
        WHERE ur.user_id = auth.uid() AND r.name = role_name