Thread: CREATE RULE problem/question requesting workaround

CREATE RULE problem/question requesting workaround

From
Chris Travers
Date:
Hi all;

I am using PostgreSQL 7.4 on RedHat 9, though I don't think that is important
to this problem.

I am attempting to write a rule that will check to see whether certain
conditions are right for a SELECT query and abort the query of those
connections are not right.  In this case, I am trying to do additional
permissions enforcement.  I have written a function to do the checking,
but when I create the rule, I get an error.  So here is the example
(the actual user-defined function is not important-- if it just returns true,
I get the same error):

CREATE OR REPLACE FUNCTION sh_fail_nonsu_admin(BOOL)
RETURNS BOOL
AS'
        DECLARE
                allow_admin ALIAS FOR $1;
                is_admin BOOL;
        BEGIN
                IF (SELECT sh_is_superuser()) THEN
                        RETURN TRUE;
                ELSEIF allow_admin == TRUE THEN
                        SELECT INTO is_admin admin FROM owners
                        WHERE login = sh_get_eff_user();

                        IF is_admin == TRUE THEN
                                RETURN TRUE;
                        END IF;
                END IF;
                RAISE EXCEPTION ''PERMISSION DENIED'';
                RETURN FALSE;
        END;
' LANGUAGE PLPGSQL;

Basically the purpose of the function is to check whether the user is a
superuser of the application (and thus able to bypass my shared-hosting
environment security system and rely on database perms instead.  As you
can see it merely checks a few options and then returns true or raises
an exception.

Now, the problem is that when I try to create the rule (something like)
CREATE OR REPLACE RULE hermes_sh_perm
AS ON SELECT TO hermes_shared_hosting.auth_shadow
DO select sh_fail_nonsu_admin(FALSE)

I get the following error:
ERROR:  rules on SELECT must have action INSTEAD SELECT

If I try to write it as an unqualified join:
CREATE OR REPLACE RULE hermes_sh_perm
AS ON SELECT TO hermes_shared_hosting.auth_shadow
DO INSTEAD SELECT * from hermes_shared_hosting.auth_shadow,
    sh_fail_nonsu_admin(FALSE)

I get the following error:
ERROR:  SELECT rule's target list has too many entries

I have tried to rewrite the rule as a CASE statement as well, but again
I get the same problem.  Is there any way to try to enforce a rewrite
rule in such a away as to do the select query as normal, but if
necessary raise an exception (to abort the select)?

Of course triggers don't work on select, so I am wondering what else can be
done.

Best Wishes,
Chris Travers



Re: CREATE RULE problem/question requesting workaround

From
Tom Lane
Date:
Chris Travers <chris@travelamericas.com> writes:
> Is there any way to try to enforce a rewrite
> rule in such a away as to do the select query as normal, but if
> necessary raise an exception (to abort the select)?

No.

You need to rethink your permissions design.  One idea that you *can*
make work is to have a view that everyone can read, but are only able to
see selected rows in.  pg_stats works that way, and I think there are
examples in the information schema as well.

            regards, tom lane

Re: CREATE RULE problem/question requesting workaround

From
Chris Travers
Date:
On Mon, 2003-12-08 at 00:46, Tom Lane wrote:
> Chris Travers <chris@travelamericas.com> writes:
> > Is there any way to try to enforce a rewrite
> > rule in such a away as to do the select query as normal, but if
> > necessary raise an exception (to abort the select)?
>
> No.
>
> You need to rethink your permissions design.  One idea that you *can*
> make work is to have a view that everyone can read, but are only able to
> see selected rows in.  pg_stats works that way, and I think there are
> examples in the information schema as well.
>
Not that this will help me at the moment, but any plans to remove the
requirement that SELECT rules be unconditional, and use DO INSTEAD?  I
can see where lifting this restriction could cause some headaches for
those implementing these sorts of rules (infinite recursion, etc), but
it would be really nice to have for these types of circumstances
(restricting the tables that must allow everyone read access to 2 or
so).

The way I would like to this implemented would be:
1) For all rules but DO INSTEAD on a select statement, the query results
are discarded.  This could allow utility functions to be "triggered" by
select statements.

2) A different option would be to allow statement-level triggers for
select queries.  (We don't need tuple-level triggers, which would
probably be overkill).

The reason I am trying to do this is because I am trying to create a
simple, lightweight layer to port a program designed for dedicated
hosting (and database permissions) into a shared hosting environment.  I
can get all permissions to be enforced aside from select permissions.
Adding every table as a view is not going to be likely to be feasible
since it will involve a major maintenance nightmare.  Depending on
requirements, we may look at allowing most of the database to be
readable to all users, but this is not ideal and I am looking for
maintainable workarounds.  I do thank you for your suggestion and we may
use it for one or 2 tables.

Best Wishes,
Chris Travers


Re: CREATE RULE problem/question requesting workaround

From
Tom Lane
Date:
Chris Travers <chris@travelamericas.com> writes:
> Adding every table as a view is not going to be likely to be feasible
> since it will involve a major maintenance nightmare.  Depending on
> requirements, we may look at allowing most of the database to be
> readable to all users, but this is not ideal and I am looking for
> maintainable workarounds.

Is it really not possible to express what you need with plain old SQL
permissions?  It seems like you are going out of your way to avoid the
obvious solution.

            regards, tom lane

Re: CREATE RULE problem/question requesting workaround

From
Chris Travers
Date:
On Mon, 2003-12-08 at 01:39, Tom Lane wrote:

>
> Is it really not possible to express what you need with plain old SQL
> permissions?  It seems like you are going out of your way to avoid the
> obvious solution.

Thanks-- as you can see, that is what the application was *designed* to
use.  The problem is porting the application which was designed under
the assumption that every user would have a database account into an
environment where that is not possible.  In the new environment, only a
few user accounts will exist and these will be used by many users.
Therefore I can only assume one user account for the entire application.

BTW, I have figured out the last of my issues, and I now have a working
solution after a EUREKA moment today...  FWIW, here is the solution.  In
writing the utilities to make this solution work, I have composed a
number of other helpful utilities which others may find of use.

Here is the solution:

CREATE SCHEMA shadow;

SELECT move_relation('my_table', 'public', 'shadow');
-- move_relation is a custom function I wrote

CREATE VIEW my_table AS
SELECT *, oid FROM shadow.my_table
WHERE (SELECT check_func());
-- I then programatically add insert, update, and delete rules
-- check_func() returns BOOL, controlling whether the query returns any
-- rows.  It can also raise an exception, causing the transaction to
-- abort.

I finally (a few moments ago) finally solved the last problem I was
having which was a permissions issue.  Now it works well and has no
known issues.  Not quite as elegant as I had hoped, but far more elegant
than I had feared.

Because this package also by necessity also contains a number of other
useful functions I may send it to the HACKERS list.

Best Wishes,
Chris Travers


Re: CREATE RULE problem/question requesting workaround

From
Tom Lane
Date:
Chris Travers <chris@travelamericas.com> writes:
> On Mon, 2003-12-08 at 01:39, Tom Lane wrote:
>> Is it really not possible to express what you need with plain old SQL
>> permissions?  It seems like you are going out of your way to avoid the
>> obvious solution.

> Thanks-- as you can see, that is what the application was *designed* to
> use.  The problem is porting the application which was designed under
> the assumption that every user would have a database account into an
> environment where that is not possible.

I guess I'm questioning the assumption that that's not possible.
Having many users share an account when you want to enforce different
permissions for each user seems like a fundamentally bad idea.  Postgres
users are sufficiently lightweight entities that I think you could and
should just make more of 'em.

            regards, tom lane

Re: CREATE RULE problem/question requesting workaround

From
Chris Travers
Date:
On Tue, 2003-12-09 at 00:54, Tom Lane wrote:
> Chris Travers <chris@travelamericas.com> writes:

>
> I guess I'm questioning the assumption that that's not possible.
> Having many users share an account when you want to enforce different
> permissions for each user seems like a fundamentally bad idea.  Postgres
> users are sufficiently lightweight entities that I think you could and
> should just make more of 'em.
>
>             regards, tom lane

Moving the system onto a server operated by a shared hosting company--
they have limits to the number of users I can use.  This is an attempt
to circumvent that limit and allow for the app to be aware of a much
larger number.  In most other web apps, they simply use a user table and
do no permissions enforcement, delegating that to the web app.

However, I don't want to go this route because the web app is more
exposed (security-wise) than the database server.  It would also require
a huge degree of retrofitting into the app.  I have actually solved all
the major technical hurdles and am in the final stages of assembling my
solution.  The solution provides for:

1:  A table of database user accounts which are allowed to bypass these
triggers (f. ex. for backup/restore of the database).

2:  A customizable view permission system which can be used to block
rows as well as queries.

3:  A number of utility functions which form the building block of these
allow you to:
    * Request a table of the column names in the table.
    * Move a table from one schema to another
    * Check to see if a relation is really a table (and/or a view).

The permissions are checked against the same catalog which is used to
store permissions metadata in the standard version of the software
(permissions are assigned in "levels" to "modules", and each "module"
usually contains many tables).  In the standard version, these
permissions are translated into database permissions and GRANTED as
appropriate.  In the shared hosting version, the permission triggers
simply check this table, so it is not that different.

Best Wishes,
Chris Travers