Thread: row-level security model

row-level security model

From
John DeSoi
Date:
I have a security model I have implemented in another (non-SQL)
database environment that I would like to use in Postgresql. I have
read the rules and set returning functions documentation but I still
don't see how it would work in Postgresql. Any ideas or direction would
be greatly appreciated.

I want to have multiple groups A, B, C where each group could only see
a subset of a table (any number of groups would be possible). If a user
is a member of groups A and B then the rows they can see should be the
union of what A and B can see. Ideally I could just write a SELECT rule
for a table or view that would somehow intersect the result rows of the
query with the result of the security function (I think Oracle has
something like this). So is it possible to write independent "access"
functions for each group and have them be dynamically combined based on
the group membership of the user? I want to do this at the database
level so the security can be enforced for any application or report
generator that is allowed to connect.

Thanks,

John DeSoi, Ph.D.


Re: row-level security model

From
Mike Mascari
Date:
John DeSoi wrote:

> I have a security model I have implemented in another (non-SQL) database
> environment that I would like to use in Postgresql. I have read the
> rules and set returning functions documentation but I still don't see
> how it would work in Postgresql. Any ideas or direction would be greatly
> appreciated.

The mechanism that has been most often described is to use
PostgreSQL user and groups and use CURRENT_USER in the view
definition. For example:

CREATE TABLE salaries (
  employee text unique not null primary key,
  salary numeric(16,2) not null,
);

CREATE VIEW v_salaries AS
SELECT *
FROM salaries
WHERE employee = CURRENT_USER;

with the appropriate GRANTs and REVOKEs applied to the view and
table. You could leverage PostgreSQL groups or join against an
application group-membership table:

CREATE VIEW v_salaries AS
SELECT *
FROM salaries
WHERE CURRENT_USER IN
  (SELECT userid
   FROM appgroups
   WHERE groupid = 'Accounting');

etc.

There are normally two issues that crop up:

1) Often people would prefer to not use PostgreSQL's authentication
mechanism, in which case CURRENT_USER is not available for view
definitions. The only way I know around this is to provide a little
'C' function to get/set a session attribute, invoke the set() upon
connecting and build the views over the get(). The set() could, for
example, take a userid and password and only actually set the global
variable accessed by get() if the password matched the application
user-table.

2) PostgreSQL allows the use of functions in WHERE clauses that can
modify the database. Oracle does not. A side effect is that if a
user has the ability to write a function, regardless of whether or
not the language is trusted, they can by-pass the use of views as
security:


http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=3D02B372.B6A4EFB6%40mascari.com&rnum=2&prev=/groups%3Fq%3DMike%2BMascari%2Bsecurity%2Bhole%26ie%3DUTF-8%26oe%3DUTF-8%26hl%3Den

HTH,

Mike Mascari








Re: row-level security model

From
Bruno Wolff III
Date:
On Wed, Mar 31, 2004 at 12:30:58 -0500,
  John DeSoi <jd@icx.net> wrote:
>
> I want to have multiple groups A, B, C where each group could only see
> a subset of a table (any number of groups would be possible). If a user
> is a member of groups A and B then the rows they can see should be the
> union of what A and B can see. Ideally I could just write a SELECT rule
> for a table or view that would somehow intersect the result rows of the
> query with the result of the security function (I think Oracle has
> something like this). So is it possible to write independent "access"
> functions for each group and have them be dynamically combined based on
> the group membership of the user? I want to do this at the database
> level so the security can be enforced for any application or report
> generator that is allowed to connect.

You should be able to do this with a view. current_user will give you
the user. You probably want to join this with your own group table
and with the table of interest. If each row belongs to only one group
this is easy.

Re: row-level security model

From
"Jim C. Nasby"
Date:
On Wed, Mar 31, 2004 at 03:53:22PM -0500, Mike Mascari wrote:
> 2) PostgreSQL allows the use of functions in WHERE clauses that can
> modify the database. Oracle does not. A side effect is that if a
> user has the ability to write a function, regardless of whether or
> not the language is trusted, they can by-pass the use of views as
> security:
>
>
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=3D02B372.B6A4EFB6%40mascari.com&rnum=2&prev=/groups%3Fq%3DMike%2BMascari%2Bsecurity%2Bhole%26ie%3DUTF-8%26oe%3DUTF-8%26hl%3Den

Yes, but Oracle has much more advanced support for row-level security.
Look for Fine-Grain Access Controll in the docs.

Also, Oracle does allow for DML in SELECT queries; look up autonomous
transactions.
--
Jim C. Nasby, Database Consultant                  jim@nasby.net
Member: Triangle Fraternity, Sports Car Club of America
Give your computer some brain candy! www.distributed.net Team #1828

Windows: "Where do you want to go today?"
Linux: "Where do you want to go tomorrow?"
FreeBSD: "Are you guys coming, or what?"

Re: row-level security model

From
John DeSoi
Date:
On Apr 1, 2004, at 10:26 AM, Bruno Wolff III wrote:

> You should be able to do this with a view. current_user will give you
> the user. You probably want to join this with your own group table
> and with the table of interest. If each row belongs to only one group
> this is easy.


I'm still not clear on how this would work. Let me provide a more
concrete example and perhaps you can give me a little more help on how
the view model would work.

Suppose I have a school database. Teachers have a many-to-many
relationship with their sections (classes). Students also have a
many-to-many relationship with the section table. So the teacher group
should be able to view the student records for any student in any of
their classes. My second group is parents. The parent and student
tables also have a many-to-many relationship. So when a parent logs in
they should be able to view any student for which they are a primary
contact. Now what if Sam is both parent and teacher groups. He should
be able to view the students in his class as well as any students for
which he is the guardian.

Best,

John DeSoi, Ph.D.


Re: row-level security model

From
John DeSoi
Date:
On Apr 1, 2004, at 10:55 AM, Jim C. Nasby wrote:

> Yes, but Oracle has much more advanced support for row-level security.
> Look for Fine-Grain Access Controll in the docs.

I found this nice article with examples on row level security with
Oracle:

This is the core functionality of the row level security
implementation. This function is what checks the context for the
current user in line with the business rules defined above and
implemented in the functions to set the security context. The function
then, based on the rights of the user executing the select statement or
update, insert or delete returns a predicate. This predicate is a
dynamic piece of SQL that is appended to the where clause of the
executing SQL by the Oracle optimizer at the time the SQL is parsed and
executed.

http://www.securityfocus.com/infocus/1743


John DeSoi, Ph.D.


Re: row-level security model

From
"Marc Durham"
Date:
From: "John DeSoi" <jd@icx.net>

>
> On Apr 1, 2004, at 10:26 AM, Bruno Wolff III wrote:
>
> > You should be able to do this with a view. current_user will give you
> > the user. You probably want to join this with your own group table
> > and with the table of interest. If each row belongs to only one group
> > this is easy.
>
>
> I'm still not clear on how this would work. Let me provide a more
> concrete example and perhaps you can give me a little more help on how
> the view model would work.
>
> Suppose I have a school database. Teachers have a many-to-many
> relationship with their sections (classes). Students also have a
> many-to-many relationship with the section table. So the teacher group
> should be able to view the student records for any student in any of
> their classes. My second group is parents. The parent and student
> tables also have a many-to-many relationship. So when a parent logs in
> they should be able to view any student for which they are a primary
> contact. Now what if Sam is both parent and teacher groups. He should
> be able to view the students in his class as well as any students for
> which he is the guardian.
>
> Best,
>
> John DeSoi, Ph.D.
>

Do you think this would work?
There are a lot of joins. And I assumed it would need to look up the
parent's and teacher's  usernames, and that your many-to-many relationships
were in tables like students_parent_link.

CREATE VIEW your_students AS
SELECT s.*
 FROM student AS s
  INNER JOIN class_student_link AS cs ON s.student_id = cs.student_id
  INNER JOIN class AS c ON cs.class_id = c.class_id
  INNER JOIN students_parent_link AS sp ON s.student_id = sp.student_id
  INNER JOIN parent AS p ON sp.parent_id = p.parent_id
  INNER JOIN teacher AS t ON c.teacher_id = t.teacher_id
WHERE t.username = CURRENT_USER()
 OR p.username = CURRENT_USER()

-Marc Durham-


Re: row-level security model

From
John DeSoi
Date:
Marc,

On Apr 1, 2004, at 4:21 PM, Marc Durham wrote:

> Do you think this would work?
> There are a lot of joins. And I assumed it would need to look up the
> parent's and teacher's  usernames, and that your many-to-many
> relationships
> were in tables like students_parent_link.
>

Thanks very much -- this helped get me started.

I think I finally came up with something that lets me have modularized
access functions where I can combine access but still maintain a single
view. Here is a rough example.

Let's say I have two access functions contact_students and
staff_teaches_students. If the current user is in the contact group it
returns the primary keys (integer dbid in my example) of the related
students. Similarly, if the current user is on the teaching staff, it
returns the keys for all students in his/her classes. So I create a
function to combine all of my access functions with union:

create or replace function student_access ()
returns setof integer as '
select * from contact_students()
union select * from staff_teaches_students();
' language sql;

Then my view is

create view student_v as select student.* from student,
student_access() as id
    where student.dbid = id;


Comments/criticisms about design or performance issues?

Is there a way to provide column security without creating different
views for every possible scenario?

Best,

John DeSoi, Ph.D.


Re: row-level security model

From
Adam Witney
Date:
On 2/4/04 4:50 am, "John DeSoi" <jd@icx.net> wrote:

> Marc,
>
> On Apr 1, 2004, at 4:21 PM, Marc Durham wrote:
>
>> Do you think this would work?
>> There are a lot of joins. And I assumed it would need to look up the
>> parent's and teacher's  usernames, and that your many-to-many
>> relationships
>> were in tables like students_parent_link.
>>
>
> Thanks very much -- this helped get me started.
>
> I think I finally came up with something that lets me have modularized
> access functions where I can combine access but still maintain a single
> view. Here is a rough example.
>
> Let's say I have two access functions contact_students and
> staff_teaches_students. If the current user is in the contact group it
> returns the primary keys (integer dbid in my example) of the related
> students. Similarly, if the current user is on the teaching staff, it
> returns the keys for all students in his/her classes. So I create a
> function to combine all of my access functions with union:
>
> create or replace function student_access ()
> returns setof integer as '
> select * from contact_students()
> union select * from staff_teaches_students();
> ' language sql;
>
> Then my view is
>
> create view student_v as select student.* from student,
> student_access() as id
> where student.dbid = id;
>
>
> Comments/criticisms about design or performance issues?
>
> Is there a way to provide column security without creating different
> views for every possible scenario?

Hi John,

I don't know if this will fit your needs, but this is how I handled row
level security in an application I have. It uses arrays, so may be
PostgreSQL specific I think... But basically I have person and group tables

CREATE TABLE person (
  person_id                            INT4            NOT NULL,
  ..
  <other fields>
  ..
  username                             TEXT            NOT NULL,
  lab_group_id                         INT4            NOT NULL,
  groups_ids                           INT[]           NULL
);

CREATE TABLE groups (
  group_id                             INT4            NOT NULL,
  name                                 TEXT            NOT NULL
);

Then each object has a base table:

CREATE TABLE experiment_base (
  expt_id                              INT4            NOT NULL,
  ..
  <other fields>
  ..
  owner_id                             INT             NOT NULL,
  writer_id                            INT[]           NOT NULL,
  readers_id                           INT[]           NOT NULL
);

I can then control who can update the row at the user level, and who can
read the row at the group level using a view like so:

CREATE OR REPLACE VIEW experiment
  AS
SELECT <various fields>
  FROM experiment_base a,
       person b
  WHERE
  a.owner_id = b.person_id AND
   (readers_id &&
    (select groups_ids from person a where a.username = current_user)
  OR
    (select person_id from person a where a.username = current_user) = ANY
(writer_id)
  OR
    owner_id = (select person_id from person a where a.username =
current_user));

I then have a couple of functions to add or remove group_id's from the
readers_id array, and also to add or remove person_id's from the writer_id
array

I don't have large numbers of users or groups, so it performs ok... Not sure
how the array approach will scale with more though.

I don't think this is a classical approach.... But it seems to work for me.
But I would appreciate comments/criticisms from others?

Cheers

Adam



--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.


Re: row-level security model

From
Bruno Wolff III
Date:
On Thu, Apr 01, 2004 at 22:50:48 -0500,
  John DeSoi <jd@icx.net> wrote:
>
> Comments/criticisms about design or performance issues?

Except for SQL functions which can be inlined, the optimizer can't
optimize what is being done in the function with what is being done
in the select statement. This might be a performance issue depending
on your particular circumstances.

> Is there a way to provide column security without creating different
> views for every possible scenario?

You should be able to have one view per table and use an OR in the where
clause to check for student, teacher or parent access. These tests
should be simple enough, that you should be able to just do the joins
in the view.

Re: row-level security model

From
Bruno Wolff III
Date:
On Thu, Apr 01, 2004 at 13:21:15 -0800,
  Marc Durham <pgsql@d-tech.com> wrote:
> From: "John DeSoi" <jd@icx.net>
>
> Do you think this would work?
> There are a lot of joins. And I assumed it would need to look up the
> parent's and teacher's  usernames, and that your many-to-many relationships
> were in tables like students_parent_link.
>
> CREATE VIEW your_students AS
> SELECT s.*
>  FROM student AS s
>   INNER JOIN class_student_link AS cs ON s.student_id = cs.student_id
>   INNER JOIN class AS c ON cs.class_id = c.class_id
>   INNER JOIN students_parent_link AS sp ON s.student_id = sp.student_id
>   INNER JOIN parent AS p ON sp.parent_id = p.parent_id
>   INNER JOIN teacher AS t ON c.teacher_id = t.teacher_id
> WHERE t.username = CURRENT_USER()
>  OR p.username = CURRENT_USER()

This makes the implicit assumption that students always have at least
one parent and at least one teacher. If that isn't necessarily true
you will need to use a couple of left (or right) joins or records of
students missing one or the other will not be accessible.

Re: row-level security model

From
John DeSoi
Date:
On Apr 2, 2004, at 10:39 AM, Bruno Wolff III wrote:

> Except for SQL functions which can be inlined, the optimizer can't
> optimize what is being done in the function with what is being done
> in the select statement. This might be a performance issue depending
> on your particular circumstances.

How does one specify a SQL function that can be inlined?

Thanks,

John DeSoi, Ph.D.


Re: row-level security model

From
Bruno Wolff III
Date:
On Fri, Apr 02, 2004 at 13:27:38 -0500,
  John DeSoi <jd@icx.net> wrote:
>
> On Apr 2, 2004, at 10:39 AM, Bruno Wolff III wrote:
>
> >Except for SQL functions which can be inlined, the optimizer can't
> >optimize what is being done in the function with what is being done
> >in the select statement. This might be a performance issue depending
> >on your particular circumstances.
>
> How does one specify a SQL function that can be inlined?

You use language 'SQL' in 7.4 or later.

Re: row-level security model

From
Bricklen
Date:
Bruno Wolff III wrote:

> On Wed, Mar 31, 2004 at 12:30:58 -0500,
>   John DeSoi <jd@icx.net> wrote:
>
>>I want to have multiple groups A, B, C where each group could only see
>>a subset of a table (any number of groups would be possible). If a user
>>is a member of groups A and B then the rows they can see should be the
>>union of what A and B can see. Ideally I could just write a SELECT rule
>>for a table or view that would somehow intersect the result rows of the
>>query with the result of the security function (I think Oracle has
>>something like this). So is it possible to write independent "access"
>>functions for each group and have them be dynamically combined based on
>>the group membership of the user? I want to do this at the database
>>level so the security can be enforced for any application or report
>>generator that is allowed to connect.

In Oracle, what you are referring to is Row Level Security, or VPD, or
whatever their marketing dept. is calling it this week.