Re: Clarification on RLS policy - Mailing list pgsql-general
From | Vydehi Ganti |
---|---|
Subject | Re: Clarification on RLS policy |
Date | |
Msg-id | CAMH-PzXe7AR2QDCDx3eRZPMGuixfTwm3sLF3kMZQkyNhtu=5qQ@mail.gmail.com Whole thread Raw |
In response to | Re: Clarification on RLS policy (Dominique Devienne <ddevienne@gmail.com>) |
Responses |
Re: Clarification on RLS policy
Re: Clarification on RLS policy Re: Clarification on RLS policy |
List | pgsql-general |
This is my Scenario:
I would need a policy on table Activity which has a column country_code .
In the policy i would need to call a function get_country as below which queries the users table based on current user and checks which country code that user has access to.
Then it should build up the lPredicate with the filter condition and append to the query user runs on the Activity table.
Can you please guide how to achieve this?
CREATE OR REPLACE FUNCTION one.get_country(
powner name,
ptable_name name)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
STABLE PARALLEL UNSAFE
AS $BODY$
DECLARE
lOSUser varchar(4000) := UPPER(SUBSTRING(current_user FROM POSITION('\' IN current_user) + 1));
lPredicate varchar(4000) := NULL;
lCount integer;
i RECORD;
BEGIN
IF position('ro' in current_user) = 0 THEN
lPredicate := '1=1';
ELSE
-- Users associated to explicit country_code
FOR i IN (SELECT r.country_code AS country_code
FROM one.users u
where UPPER(SUBSTR(u.Login, INSTR(u.Login, '\', -1) + 1)) = lOSUser )
WHERE u.role_type = 'reader') LOOP
lPredicate := lPredicate||''''||i.country_code||''',';
END LOOP;
IF lPredicate IS NOT NULL THEN
lPredicate := 'SUBSTR("id",1,3) IN ('||SUBSTR(lPredicate, 1, LENGTH(lPredicate)-1)||')';
ELSE
lPredicate := '1=1';
END IF;
END IF;
RETURN lPredicate;
END;
$BODY$;
For the below policy statement it created the policy but i cant call that
CREATE POLICY "Codebase_Filter"
ON one.activity
FOR SELECT
TO one
USING (one.get_country('one','activity'));
I would need a policy on table Activity which has a column country_code .
In the policy i would need to call a function get_country as below which queries the users table based on current user and checks which country code that user has access to.
Then it should build up the lPredicate with the filter condition and append to the query user runs on the Activity table.
Can you please guide how to achieve this?
CREATE OR REPLACE FUNCTION one.get_country(
powner name,
ptable_name name)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
STABLE PARALLEL UNSAFE
AS $BODY$
DECLARE
lOSUser varchar(4000) := UPPER(SUBSTRING(current_user FROM POSITION('\' IN current_user) + 1));
lPredicate varchar(4000) := NULL;
lCount integer;
i RECORD;
BEGIN
IF position('ro' in current_user) = 0 THEN
lPredicate := '1=1';
ELSE
-- Users associated to explicit country_code
FOR i IN (SELECT r.country_code AS country_code
FROM one.users u
where UPPER(SUBSTR(u.Login, INSTR(u.Login, '\', -1) + 1)) = lOSUser )
WHERE u.role_type = 'reader') LOOP
lPredicate := lPredicate||''''||i.country_code||''',';
END LOOP;
IF lPredicate IS NOT NULL THEN
lPredicate := 'SUBSTR("id",1,3) IN ('||SUBSTR(lPredicate, 1, LENGTH(lPredicate)-1)||')';
ELSE
lPredicate := '1=1';
END IF;
END IF;
RETURN lPredicate;
END;
$BODY$;
For the below policy statement it created the policy but i cant call that
CREATE POLICY "Codebase_Filter"
ON one.activity
FOR SELECT
TO one
USING (one.get_country('one','activity'));
On Fri, Apr 25, 2025 at 6:23 PM Dominique Devienne <ddevienne@gmail.com> wrote:
On Fri, Apr 25, 2025 at 2:43 PM Laurenz Albe <laurenz.albe@cybertec.at> wrote:
> On Fri, 2025-04-25 at 12:38 +0530, Vydehi Ganti wrote:
> > We are presently using Postgresql:PostgreSQL 15.12 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-23), 64-bit
> > 2.The function would return a character varying string which should be appended
> > to the select as a filter.
>
> You cannot add whole WHERE conditions to a query dynamically.
> The only way to fix that is to solve the problem differently.
> Since you didn't tell us details, we cannot tell you how.
Laurenz is right. That's not how RLS works in PostgreSQL, unlike Oracle.
In PostgreSQL, you must use a boolean SQL expression, often by calling
a function.
You don't simply return some SQL text that Oracle then "splices" into
the SELECT.
E.g., if you use custom ROLEs as an implementation detail for your
security rules,
your policy can be as simple as calling the pg_has_role() built-in
function. FWIW. --DD
CREATE POLICY ... USING (pg_has_role('SomeRole', 'MEMBER'))
pgsql-general by date: