Hey everyone, question on RLS policies. I have two...
# help
v
Hey everyone, question on RLS policies. I have two types of users, regular and admin. Regular users can manage their own records only, while admins can manage all records. I was able to do this on a records table with the following policy. (uid() IN ( SELECT profiles.id FROM profiles WHERE (profiles.is_admin = true))) However this doesn't work for the profiles table. Any ideas on what I'm doing wrong?
n
Hello @victorwhiskey! This thread has been automatically created from your message in #843999948717555735 a few seconds ago. We have already mentioned the @User so that they can see your message and help you as soon as possible! Want to unsubscribe from this thread? Right-click the thread in Discord (or use the ``...`` menu) and select "Leave Thread" to unsubscribe from future updates. Want to change the title? Use the ``/title`` command! We have solved your problem? Click the button below to archive it.
v
The policy on Admins is as follows
(uid() IN ( SELECT profiles_1.id FROM profiles profiles_1 WHERE (profiles_1.is_admin = true)))
which results in the following error
"infinite recursion detected in policy for relation "profiles""
g
You can’t have RLS do a select on its own table which then runs RLS again, recursively.
n
victorwhiskey (2022-05-16)
g
s
You could potentially simplify it to this on the profiles table:
Copy code
sql
(id === uid() OR is_admin)
This requires that your profile table has an
id
column which is set to the corresponding UID record in the
auth.users
table, and an
is_admin
boolean column Conditions output if we look at it on an individual row basis: - ID matches, user is admin: Can access all rows - ID matches, user is not admin: Can access this row - ID doesn't match, user is admin: Can access all rows - ID doesn't match, user is not admin: Can not access this row
v
This is what I need, I'll give it a try. Thanks!
Doesn't seem to work unfortunately. How do I specify that is_admin value is coming from the auth.user's profile rather than the profile I'm trying to access?
It seems that having records accessible by either their creator or an admin would be a pretty common use case. Is there a better way to accomplish this?
Turtles all the way down, I see now.
Ah, got it. Since that is the case, how do I implement this...can I alter the auth table to add the is_admin field?
b
You're better off creating a table in the public schema like public.user_data or something like that
and link it to auth.users on the id field
then use that to track it...
or you can use a custom claim...
v
That was the intent for the profiles table, but apparently I can't have the is_admin column there
b
I have some info on how to do that...then it gets stored in the auth.users record in the app_metadata field
v
seems like that might be the best way to do it
That'll require you use the service key since it's a server process.
v
Cool thanks, I'll give that a try!
b
Then you can get the field with something like this:
Copy code
CREATE OR REPLACE FUNCTION auth.get_claims() RETURNS "jsonb"
    LANGUAGE "sql" STABLE
    AS $$
  select 
      coalesce(nullif(current_setting('request.jwt.claims', true), '')::jsonb -> 'app_metadata', '{}'::jsonb)::jsonb
$$;
That'll get all the custom stuff, but you can customize it to get your specific field.
Once you have this function, you can use it inside your RLS policies and it'll be really fast, since it doesn't need to hit the database.