Thread: creating variable views

creating variable views

From
Dado Feigenblatt
Date:
Hi. New to the list.

I'm building a database that will hold data for many different projects.
Some people, or groups of people, will have access to just the rows of data of their
projects.
Some are very granular. Let's use for this example the data about the people itself.
Other than the administrators, I want people to see only their own data.
Instead of creating a view for each person, is it possible to create a single view with
variable data?

CREATE VIEW user_info AS
SELECT * FROM users
WHERE user_name = pg_user

where pg_user is the user name that person used to log into the database.
Is there a way to get the user name in Postgresql?
Even if the variable pg_user is not available,
is it possible to create views using variables like that?

Thanks.

--
Dado Feigenblatt                                 Wild Brain, Inc.
Technical Director                               (415) 553-8000 x???
dado@wildbrain.com                               San Francisco, CA.





Re: creating variable views

From
"Richard Huxton"
Date:
From: "Dado Feigenblatt" <dado@wildbrain.com>

> Hi. New to the list.

Welcome aboard :-)

> I'm building a database that will hold data for many different projects.
> Some people, or groups of people, will have access to just the rows of
data of their
> projects.
> Some are very granular. Let's use for this example the data about the
people itself.
> Other than the administrators, I want people to see only their own data.
> Instead of creating a view for each person, is it possible to create a
single view with
> variable data?
>
> CREATE VIEW user_info AS
> SELECT * FROM users
> WHERE user_name = pg_user
>
> where pg_user is the user name that person used to log into the database.
> Is there a way to get the user name in Postgresql?
> Even if the variable pg_user is not available,
> is it possible to create views using variables like that?

Nice idea, and seems to work:

richardh=> \c richardh richardh
You are now connected to database richardh as user richardh.
richardh=> \d usertest              Table "usertest"Attribute |         Type          | Modifier
-----------+-----------------------+----------username  | character varying(64) | not nullnum       | integer
   |
 
Index: usertest_name_idx

richardh=> \d utview               View "utview"Attribute |         Type          | Modifier
-----------+-----------------------+----------username  | character varying(64) |num       | integer               |
View definition: SELECT usertest.username, usertest.num FROM usertest WHERE
(name(usertest.username) = "current_user"());

richardh=> select * from usertest;username | num
----------+-----richardh |   1andy     |   2
(2 rows)

richardh=> select * from utview;username | num
----------+-----richardh |   1
(1 row)

richardh=> \c richardh andy
You are now connected to database richardh as user andy.
richardh=> select * from usertest;
ERROR:  usertest: Permission denied.
richardh=> select * from utview;username | num
----------+-----andy     |   2
(1 row)

richardh=> select version();                          version
-------------------------------------------------------------PostgreSQL 7.1.1 on i586-pc-linux-gnu, compiled by GCC
2.96



Interesting (to me anyway) to note that the view definition is based on a
compiled query, not what I typed since I used "username::name" in the cast
and CURRENT_USER rather than current_user()

HTH

- Richard Huxton



Re: creating variable views

From
"Dado Feigenblatt"
Date:
From: "Richard Huxton" <dev@archonet.com>

> From: "Dado Feigenblatt" <dado@wildbrain.com>
>
> > Hi. New to the list.
>
> Welcome aboard :-)
>
> > I'm building a database that will hold data for many different projects.
> > Some people, or groups of people, will have access to just the rows of
> data of their
> > projects.
> > Some are very granular. Let's use for this example the data about the
> people itself.
> > Other than the administrators, I want people to see only their own data.
> > Instead of creating a view for each person, is it possible to create a
> single view with
> > variable data?
> >
> > CREATE VIEW user_info AS
> > SELECT * FROM users
> > WHERE user_name = pg_user
> >
> > where pg_user is the user name that person used to log into the
database.
> > Is there a way to get the user name in Postgresql?
> > Even if the variable pg_user is not available,
> > is it possible to create views using variables like that?
>
> Nice idea, and seems to work:

Thanks :)

> richardh=> \c richardh richardh
> You are now connected to database richardh as user richardh.
> richardh=> \d usertest
>                Table "usertest"
>  Attribute |         Type          | Modifier
> -----------+-----------------------+----------
>  username  | character varying(64) | not null
>  num       | integer               |
> Index: usertest_name_idx
>
> richardh=> \d utview
>                 View "utview"
>  Attribute |         Type          | Modifier
> -----------+-----------------------+----------
>  username  | character varying(64) |
>  num       | integer               |
> View definition: SELECT usertest.username, usertest.num FROM usertest
WHERE
> (name(usertest.username) = "current_user"());

CURRENT_USER !  That's how it's called, uh?

> richardh=> select * from usertest;
>  username | num
> ----------+-----
>  richardh |   1
>  andy     |   2
> (2 rows)
>
> richardh=> select * from utview;
>  username | num
> ----------+-----
>  richardh |   1
> (1 row)
>
> richardh=> \c richardh andy
> You are now connected to database richardh as user andy.
> richardh=> select * from usertest;
> ERROR:  usertest: Permission denied.
> richardh=> select * from utview;
>  username | num
> ----------+-----
>  andy     |   2
> (1 row)
>
> richardh=> select version();
>                            version
> -------------------------------------------------------------
>  PostgreSQL 7.1.1 on i586-pc-linux-gnu, compiled by GCC 2.96
>
>
>
> Interesting (to me anyway) to note that the view definition is based on a
> compiled query, not what I typed since I used "username::name" in the cast
> and CURRENT_USER rather than current_user()
>
> HTH
>
> - Richard Huxton

I remember reading somewhere that these queries would be compiled,
improving performance on views a little bit.

Funny that current_user is a function, but one should not append the ()'s to
it.
And how did you know to type cast username::name ?  Tricks of the trade?
It says in the documentation that

"The name type exists only for storage of internal catalog names and is not
intended for use by the general user. "

Well, it looks like I can replace 'current_user' by any function and not
have to worry about creating views every time I get a new user, group, or
project. That's very good! Unless... is there any current_group() ?
How do I find which group a user belongs to? Hmmm... maybe I don't need
that/

Lastly, any pointer on how people go about managing that kind of access? I
mean, gazzillions of tables with related data spread all over, and many
different groups of people with different access levels to certain rows on
certain tables?

That's it for now.
Thanks a lot.

Dado Feigenblatt.
dado@wildbrain.com





RE:creating variable views

From
"Josh Berkus"
Date:
Dado,

    Glad to have you with us.   Incidentally, in answer to two of your
remarks:

1. "current_user", like "current_timestamp" is a built-in SQL92
function, as opposed to a PostgreSQL function.  As such, it does not
require () and takes no parameters.

2. You can compile any user-defined function into a view, wherever you
want.  Be warned, though, that views with lots of user-defined functions
suffer from a certain lack of optimization as compared with pure SQL
views.  As such, I try to stick with strictly formatting functions in
views.

> Lastly, any pointer on how people go about managing that kind of
> access? I
> mean, gazzillions of tables with related data spread all over, and
> many
> different groups of people with different access levels to certain
> rows on
> certain tables?

Actually, in every project I've undertaken, I avoid using the built-in
DB security and create my own security interfaces.  This is because, for
an end-user program, you are concered with the user's access to
*interfaces*, not their access to *tables*.  In only two occasions can I
imagine DB security making any sense for a user application:

1. The users are SQL experts and want to run their own queires, and have
to be kept away from specific sensitive data.

2. The users have access to certain 3rd-party tools that need to bypass
the regular interface (e.g. IQ Reports, FRx) and the database contains
specific tables of sensitive data.

Additionally, it must be noted that trying to combine a Web interface
with specfic user DB logins eliminates all ability to pool DB
connections and similar web efficiency, as well as forcing you to use
SSL for any extranet app.

Otherwise, one takes these steps:

1. Create a table of users, passwords, and access levels for your app.

2. Create a second table of interfaces and special functions and the
access levels required to reach them.

3. Build your interface so that it connects to the database using a
single super-user login which is kept encrypted and hidden from the
user.

4. In the interface, before letting the user open each screen or run
each function, check their user access against the tables in 1. and 2.

This works quite well for me.  It's a *lot* easier to adjust than DB
level security ("all of the accountants need access to the Void
function" can be fixed with a single UPDATE) and remains secure because
the *user* does not know the application password, and without it has no
access to the database at all.

-Josh Berkus

P.S. Keep up the fun cartoons!

______AGLIO DATABASE SOLUTIONS___________________________
                                       Josh Berkus
  Complete information technology      josh@agliodbs.com
   and data management solutions       (415) 565-7293
  for law firms, small businesses        fax 621-2533
    and non-profit organizations.      San Francisco

Attachment

Re: creating variable views

From
Dado Feigenblatt
Date:
Josh Berkus wrote:

> Dado,
>
>         Glad to have you with us.   Incidentally, in answer to two of your
> remarks:
>
> 1. "current_user", like "current_timestamp" is a built-in SQL92
> function, as opposed to a PostgreSQL function.  As such, it does not
> require () and takes no parameters.

Hmm... I don't recall any reference to that. Thanks for the clarification.

> 2. You can compile any user-defined function into a view, wherever you
> want.  Be warned, though, that views with lots of user-defined functions
> suffer from a certain lack of optimization as compared with pure SQL
> views.  As such, I try to stick with strictly formatting functions in
> views.

Formatting functions? As in formatted output? Could you give an example?

> > Lastly, any pointer on how people go about managing that kind of
> > access? I
> > mean, gazzillions of tables with related data spread all over, and
> > many
> > different groups of people with different access levels to certain
> > rows on
> > certain tables?
>
> Actually, in every project I've undertaken, I avoid using the built-in
> DB security and create my own security interfaces.  This is because, for
> an end-user program, you are concered with the user's access to
> *interfaces*, not their access to *tables*.  In only two occasions can I
> imagine DB security making any sense for a user application:
>
> 1. The users are SQL experts and want to run their own queires, and have
> to be kept away from specific sensitive data.
>
> 2. The users have access to certain 3rd-party tools that need to bypass
> the regular interface (e.g. IQ Reports, FRx) and the database contains
> specific tables of sensitive data.
>
> Additionally, it must be noted that trying to combine a Web interface
> with specfic user DB logins eliminates all ability to pool DB
> connections and similar web efficiency, as well as forcing you to use
> SSL for any extranet app.

Ok. Looks like I agree with you in every aspect.
Better yet, it's good ammunition for me to explain here why I'm gonna do they
way you suggested.

> Otherwise, one takes these steps:
>
> 1. Create a table of users, passwords, and access levels for your app.
>
> 2. Create a second table of interfaces and special functions and the
> access levels required to reach them.
>
> 3. Build your interface so that it connects to the database using a
> single super-user login which is kept encrypted and hidden from the
> user.
>
> 4. In the interface, before letting the user open each screen or run
> each function, check their user access against the tables in 1. and 2.
>
> This works quite well for me.  It's a *lot* easier to adjust than DB
> level security ("all of the accountants need access to the Void
> function" can be fixed with a single UPDATE)

I'm sorry but I have no idea what you're talking about here.
What is this problem? What is the Void function?

> and remains secure because
> the *user* does not know the application password, and without it has no
> access to the database at all.

It definitely seems to be a much better approach to access level management.
One thing that still isn't clear for me is how to implement access level
control on a per row basis.
Perhaps by implementing a group permissions scheme, where I could combine
groups that describe a job title (and its granted permissions) with groups
that describe projects (and its required permissions).
But still, I'm not sure if I should implement that on the interface or use
views that select rows pertaining only to the user's projects.

Any pointer on that would be immensely appreciated.
But the info you already gave is invaluable.
Thanks a lot.

> -Josh Berkus
>
> P.S. Keep up the fun cartoons!

That's what I really do.
I got pulled out to create this DB just because I worked with DB's before.
10 years ago :(

--
Dado Feigenblatt                                 Wild Brain, Inc.
Technical Director                               (415) 553-8000 x???
dado@wildbrain.com                               San Francisco, CA.





Re: creating variable views

From
"Josh Berkus"
Date:
Dado,
> Formatting functions? As in formatted output? Could you give an
> example?

Yes.  For example, I have a function called:

qf_format_contact_name (VARCHAR, VARCHAR, VARCHAR, VARCHAR) 

That produces (depending on data) formatted output like:

Julie Snodgrass
Ms. Keller, Law Clerk
Human Resources Director

... and I call in in views like:

CREATE VIEW lv_billing_contacts AS
SELECT client_usq, client_name, qf_format_contact_name(prefix,
last_name, first_name, contact_title) AS contact_name
FROM clients JOIN client_contacts ...

All this function does is format output, rather than perform any fancy
manipulation.  I find that the Postgres view optimizer has no trouble
with such functions.

More complex functions, like qf_calc_next_invoice_date(VARCHAR) which
calculates a client's next invoice date based on their invoice interval
plus certain system variables pretty much kills the view optimizer if I
do a WHERE on that column, since the optimizer doesn't know what to
expect from the function.



> > This works quite well for me.  It's a *lot* easier to adjust than
> DB
> > level security ("all of the accountants need access to the Void
> > function" can be fixed with a single UPDATE)
> 
> I'm sorry but I have no idea what you're talking about here.
> What is this problem? What is the Void function?

That was an example of the sort of sweeping user access change one might
be asked to implement.  For example, you might set up the system at the
start so that only the Sysadmin can "void" (cancel) financial
transactions for security purposes.  However, changes in your company's
business policies in 3 months may dictate that the whole accounting
dept.  needs to be able to void.  Using SQL DB security, this can be a
serious headache, as opposed to a single update with an interface-based
system.

-Josh


______AGLIO DATABASE SOLUTIONS___________________________                                      Josh Berkus Complete
informationtechnology      josh@agliodbs.com  and data management solutions       (415) 565-7293 for law firms, small
businesses       fax 621-2533   and non-profit organizations.      San Francisco
 


Re: creating variable views

From
Tom Lane
Date:
Dado Feigenblatt <dado@wildbrain.com> writes:
> One thing that still isn't clear for me is how to implement access level
> control on a per row basis.

The SQL GRANT/REVOKE stuff doesn't deal with anything finer-grain than
tables.  The best way I know to cope with a need for row-level read
access control is to create a view that shows only the records you want
someone to be able to see, then grant them access to the view not the
original table.

For write access control, you have a choice of putting the controls into
the ON INSERT etc rules you make for the view, or attaching triggers to
the underlying table and checking access permissions in the triggers.
The trigger method is probably easier to deal with, but bear in mind
that such triggers will fire for everyone, including people who've been
granted direct access to the underlying table.  This might or might not
be just what you want...
        regards, tom lane


Re: creating variable views

From
Tom Lane
Date:
"Josh Berkus" <josh@agliodbs.com> writes:
> All this function does is format output, rather than perform any fancy
> manipulation.  I find that the Postgres view optimizer has no trouble
> with such functions.
> More complex functions, like qf_calc_next_invoice_date(VARCHAR) which
> calculates a client's next invoice date based on their invoice interval
> plus certain system variables pretty much kills the view optimizer if I
> do a WHERE on that column, since the optimizer doesn't know what to
> expect from the function.

I don't understand the distinction you're trying to make here.  In
general, a view column that is implemented as a function will give the
optimizer headaches if you refer to it in WHERE --- the simplicity or
complexity of the function has got nothing to do with that AFAICS.

> That was an example of the sort of sweeping user access change one might
> be asked to implement.  For example, you might set up the system at the
> start so that only the Sysadmin can "void" (cancel) financial
> transactions for security purposes.  However, changes in your company's
> business policies in 3 months may dictate that the whole accounting
> dept.  needs to be able to void.  Using SQL DB security, this can be a
> serious headache, as opposed to a single update with an interface-based
> system.

Seems to me that such a change could be trivial, or a serious headache,
with *either* SQL GRANT-based security or application-based security.
The critical factor is going to be whether you represented the "voiding"
access privilege separately from the other special privileges of the
sysadmin.  I don't see how one implementation is going to encourage you
to have that foresight better than the other one would.
        regards, tom lane


Re: creating variable views

From
"Josh Berkus"
Date:
Tom,

> I don't understand the distinction you're trying to make here.  In
> general, a view column that is implemented as a function will give
> the
> optimizer headaches if you refer to it in WHERE --- the simplicity or
> complexity of the function has got nothing to do with that AFAICS.

OK.  I thought that I noticed a difference between simple SQL functions
and PL/pgSQL functions in this respect, but I could easily be mistaken.
> Seems to me that such a change could be trivial, or a serious
> headache,
> with *either* SQL GRANT-based security or application-based security.
> The critical factor is going to be whether you represented the
> "voiding"
> access privilege separately from the other special privileges of the
> sysadmin.  I don't see how one implementation is going to encourage
> you
> to have that foresight better than the other one would.

It's a little difficult to explain wihtout a demo.  It is possible,
using a database with many views and functions which are the primary
methods of DML and query access to develop analogous functionality using
GRANT and REVOKE on the database objects.  In fact, you *have* to do
this if your users will have command-line access to the database.
However, it's not easy.

The interface-based system I use, in its simplest incarnation, takes
into account only a 5-level user access system with no departmental user
groups.  Thus each user is: 0:No Access, 1:Read-only, 2:Data Entry,
3:Full Access, or 5:Admin.  I create a table that lists all of the
interfaces, and the required access level for each interface; if a user
fails the access test for an interface, they are denied access with a
firendly error message ("I'm sorry, you do not have sufficient access
...").

It's very simple to administrate because in order to change the access
to a particular feature on has only to change the access level number.
And the results of the access level test are much easier to trap in your
client-side code than the results of a DENY access error would be.  The
last thing I want for my users is to have to contend with "5301: No
rights on TABLE client_contacts."

-Josh


______AGLIO DATABASE SOLUTIONS___________________________                                      Josh Berkus Complete
informationtechnology      josh@agliodbs.com  and data management solutions       (415) 565-7293 for law firms, small
businesses       fax 621-2533   and non-profit organizations.      San Francisco
 


Re: creating variable views

From
Tom Lane
Date:
"Josh Berkus" <josh@agliodbs.com> writes:
>> I don't understand the distinction you're trying to make here.  In
>> general, a view column that is implemented as a function will give
>> the
>> optimizer headaches if you refer to it in WHERE --- the simplicity or
>> complexity of the function has got nothing to do with that AFAICS.

> OK.  I thought that I noticed a difference between simple SQL functions
> and PL/pgSQL functions in this respect, but I could easily be mistaken.

AFAIR, the optimizer doesn't pay any attention at all to the
implementation language of a function.  The only thing that comes to
mind here is that some care has been taken to mark all the built-in
functions as "iscachable" (or not, as appropriate); but user-created
functions may not be so marked when they should be, leading to loss of
performance in some cases.
        regards, tom lane