Thread: question on row level security

question on row level security

From
Tim Dudgeon
Date:
The new row level security feature in 9.5 looks great.
I guess its designed around the need to restrict access based on the 
current database user (current_user) where this maps to a database user.
But most applications now access the database using an application user 
and manages data for the applications multiple users (probably with each 
user being a row in a USERS table somewhere).
Is there any way to "inject" the application user so that this can be 
used in a RLS check?
e.g. conceptually:

set app_user 'john';
select * from foo;

where the select * is restricted by a RLS check that includes 'john' as 
the app_user.
Of course custom SQL could be generated for this, but it would be safer 
if it could be handled using RLS.

Any ways to do this?

Tim




Re: question on row level security

From
"David G. Johnston"
Date:
On Wed, Dec 30, 2015 at 9:58 AM, Tim Dudgeon <tdudgeon.ml@gmail.com> wrote:
The new row level security feature in 9.5 looks great.
I guess its designed around the need to restrict access based on the current database user (current_user) where this maps to a database user.
But most applications now access the database using an application user and manages data for the applications multiple users (probably with each user being a row in a USERS table somewhere).
Is there any way to "inject" the application user so that this can be used in a RLS check?
e.g. conceptually:

set app_user 'john';
select * from foo;

where the select * is restricted by a RLS check that includes 'john' as the app_user.
Of course custom SQL could be generated for this, but it would be safer if it could be handled using RLS.

Any ways to do this
​?

​Does this address your concerns?

​"""
The session_user is normally the user who initiated the current database connection; but superusers can change this setting with SET SESSION AUTHORIZATION. The current_user is the user identifier that is applicable for permission checking. Normally it is equal to the session user, but it can be changed with SET ROLE. It also changes during the execution of functions with the attribute SECURITY DEFINER. In Unix parlance, the session user is the "real user" and the current user is the "effective user".
"""


RLS uses "current_user" when performing checks.

David J.

Re: question on row level security

From
Adrian Klaver
Date:
On 12/30/2015 08:58 AM, Tim Dudgeon wrote:
> The new row level security feature in 9.5 looks great.
> I guess its designed around the need to restrict access based on the
> current database user (current_user) where this maps to a database user.
> But most applications now access the database using an application user
> and manages data for the applications multiple users (probably with each
> user being a row in a USERS table somewhere).
> Is there any way to "inject" the application user so that this can be
> used in a RLS check?
> e.g. conceptually:
>
> set app_user 'john';
> select * from foo;
>
> where the select * is restricted by a RLS check that includes 'john' as
> the app_user.
> Of course custom SQL could be generated for this, but it would be safer
> if it could be handled using RLS.
>
> Any ways to do this?

User name maps?:

http://www.postgresql.org/docs/9.5/interactive/auth-username-maps.html

This still results in an external user becoming a database user. From 
there you can set up users as members of larger roles, i.e accounting, 
hr, etc to manage access, or not.

>
> Tim
>
>
>


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: question on row level security

From
Tim Dudgeon
Date:
On 30/12/2015 17:19, David G. Johnston wrote:
On Wed, Dec 30, 2015 at 9:58 AM, Tim Dudgeon <tdudgeon.ml@gmail.com> wrote:
The new row level security feature in 9.5 looks great.
I guess its designed around the need to restrict access based on the current database user (current_user) where this maps to a database user.
But most applications now access the database using an application user and manages data for the applications multiple users (probably with each user being a row in a USERS table somewhere).
Is there any way to "inject" the application user so that this can be used in a RLS check?
e.g. conceptually:

set app_user 'john';
select * from foo;

where the select * is restricted by a RLS check that includes 'john' as the app_user.
Of course custom SQL could be generated for this, but it would be safer if it could be handled using RLS.

Any ways to do this
​ ?

​ Does this address your concerns?

​ """
The session_user is normally the user who initiated the current database connection; but superusers can change this setting with SET SESSION AUTHORIZATION. The current_user is the user identifier that is applicable for permission checking. Normally it is equal to the session user, but it can be changed with SET ROLE. It also changes during the execution of functions with the attribute SECURITY DEFINER. In Unix parlance, the session user is the "real user" and the current user is the "effective user".
"""


RLS uses "current_user" when performing checks.

David J.


It might, but does it mean that that user (the app_user in my original question) still has to be a regular database user (e.g. one who has a database account and can connect to the database)? This is what I want to avoid.

Tim

Re: question on row level security

From
Joe Conway
Date:
On 12/30/2015 08:58 AM, Tim Dudgeon wrote:
> e.g. conceptually:
>
> set app_user 'john';
> select * from foo;
>
> where the select * is restricted by a RLS check that includes 'john' as
> the app_user.
> Of course custom SQL could be generated for this, but it would be safer
> if it could be handled using RLS.
>
> Any ways to do this?

Something like this:

8<--------------------------
CREATE USER application;

CREATE TABLE t1 (id int primary key, f1 text, app_user text);
INSERT INTO t1 VALUES(1,'a','bob');
INSERT INTO t1 VALUES(2,'b','alice');
ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
CREATE POLICY P ON t1 USING (app_user =
current_setting('app_name.app_user'));
GRANT SELECT ON t1 TO application;

SET SESSION AUTHORIZATION application;

regression=> SET app_name.app_user = 'bob';
SET
regression=> SELECT * FROM t1;id | f1 | app_user
----+----+---------- 1 | a  | bob
(1 row)

regression=> SET app_name.app_user = 'alice';
SET
regression=> SELECT * FROM t1;id | f1 | app_user
----+----+---------- 2 | b  | alice
(1 row)

regression=> SET app_name.app_user = 'none';
SET
regression=> SELECT * FROM t1;id | f1 | app_user
----+----+----------
(0 rows)

8<--------------------------

HTH,

Joe

--
Crunchy Data - http://crunchydata.com
PostgreSQL Support for Secure Enterprises
Consulting, Training, & Open Source Development


Re: question on row level security

From
Karsten Hilbert
Date:
On Wed, Dec 30, 2015 at 05:28:13PM +0000, Tim Dudgeon wrote:

> >    The new row level security feature in 9.5 looks great.
> >    I guess its designed around the need to restrict access based on
> >    the current database user (current_user) where this maps to a
> >    database user.
> >    But most applications now access the database using an application
> >    user and manages data for the applications multiple users
> >    (probably with each user being a row in a USERS table somewhere).
> >    Is there any way to "inject" the application user so that this can
> >    be used in a RLS check?
> >    e.g. conceptually:
> >
> >    set app_user 'john';
> >    select * from foo;
> >
> >    where the select * is restricted by a RLS check that includes
> >    'john' as the app_user.
> >    Of course custom SQL could be generated for this, but it would be
> >    safer if it could be handled using RLS.
> >
> >    Any ways to do this

You could store a session cookie (say, the app_user) into a
table and have the RLS policy refer to that, no ?

Karsten
-- 
GPG key ID E4071346 @ eu.pool.sks-keyservers.net
E167 67FD A291 2BEA 73BD  4537 78B9 A9F9 E407 1346



Re: question on row level security

From
Tim Dudgeon
Date:
On 30/12/2015 17:32, Joe Conway wrote:
> On 12/30/2015 08:58 AM, Tim Dudgeon wrote:
>> e.g. conceptually:
>>
>> set app_user 'john';
>> select * from foo;
>>
>> where the select * is restricted by a RLS check that includes 'john' as
>> the app_user.
>> Of course custom SQL could be generated for this, but it would be safer
>> if it could be handled using RLS.
>>
>> Any ways to do this?
> Something like this:
>
> 8<--------------------------
> CREATE USER application;
>
> CREATE TABLE t1 (id int primary key, f1 text, app_user text);
> INSERT INTO t1 VALUES(1,'a','bob');
> INSERT INTO t1 VALUES(2,'b','alice');
> ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
> CREATE POLICY P ON t1 USING (app_user =
> current_setting('app_name.app_user'));
> GRANT SELECT ON t1 TO application;
>
> SET SESSION AUTHORIZATION application;
>
> regression=> SET app_name.app_user = 'bob';
> SET
> regression=> SELECT * FROM t1;
>   id | f1 | app_user
> ----+----+----------
>    1 | a  | bob
> (1 row)
>
> regression=> SET app_name.app_user = 'alice';
> SET
> regression=> SELECT * FROM t1;
>   id | f1 | app_user
> ----+----+----------
>    2 | b  | alice
> (1 row)
>
> regression=> SET app_name.app_user = 'none';
> SET
> regression=> SELECT * FROM t1;
>   id | f1 | app_user
> ----+----+----------
> (0 rows)
>
> 8<--------------------------
>
> HTH,
>
> Joe
>
Looks like that's what I need.
I'll give it a try.

Thanks
Tim



Re: question on row level security

From
"David G. Johnston"
Date:
On Wed, Dec 30, 2015 at 10:32 AM, Joe Conway <mail@joeconway.com> wrote:
On 12/30/2015 08:58 AM, Tim Dudgeon wrote:
> e.g. conceptually:
>
> set app_user 'john';
> select * from foo;
>
> where the select * is restricted by a RLS check that includes 'john' as
> the app_user.
> Of course custom SQL could be generated for this, but it would be safer
> if it could be handled using RLS.
>
> Any ways to do this?

Something like this:

8<--------------------------
CREATE USER application;

CREATE TABLE t1 (id int primary key, f1 text, app_user text);
INSERT INTO t1 VALUES(1,'a','bob');
INSERT INTO t1 VALUES(2,'b','alice');
ALTER TABLE t1 ENABLE ROW LEVEL SECURITY;
CREATE POLICY P ON t1 USING (app_user =
current_setting('app_name.app_user'));
GRANT SELECT ON t1 TO application;

SET SESSION AUTHORIZATION application;

regression=> SET app_name.app_user = 'bob';
SET
regression=> SELECT * FROM t1;
 id | f1 | app_user
----+----+----------
  1 | a  | bob
(1 row)

regression=> SET app_name.app_user = 'alice';
SET
regression=> SELECT * FROM t1;
 id | f1 | app_user
----+----+----------
  2 | b  | alice
(1 row)

regression=> SET app_name.app_user = 'none';
SET
regression=> SELECT * FROM t1;
 id | f1 | app_user
----+----+----------
(0 rows)

8<--------------------------

HTH,

Joe


​I think an example like this, emphasizing the use of something other than current_user, should be considered for the documentation @


David J.