Re: ExecutorCheckPerms() hook - Mailing list pgsql-hackers

From Stephen Frost
Subject Re: ExecutorCheckPerms() hook
Date
Msg-id 20100526134202.GW21875@tamriel.snowman.net
Whole thread Raw
In response to Re: ExecutorCheckPerms() hook  (KaiGai Kohei <kaigai@ak.jp.nec.com>)
List pgsql-hackers
KaiGai,

* KaiGai Kohei (kaigai@ak.jp.nec.com) wrote:
> Yes, it is entirely separate issue. I don't intend to argue whether
> we can assume the default PG permission allows owner to SELECT on
> the table, or not.

This actually isn't a separate issue.  It's the whole crux of it, as a
matter of fact.  So, wrt the standard, you do NOT need SELECT rights on
a table to create an FK against it.  You only need references.  PG
handles this correctly.

This error:

> >>    postgres=>  ALTER TABLE fk_tbl ADD FOREIGN KEY (x) REFERENCES pk_tbl (a);
> >>    ERROR:  permission denied for relation pk_tbl
> >>    CONTEXT:  SQL statement "SELECT 1 FROM ONLY "public"."pk_tbl" x WHERE "a" OPERATOR(pg_catalog.=) $1 FOR SHARE
OFx"
 

Is due to the *owner* of pk_tbl not having SELECT rights- a situation
we're not generally expecting to see.  What's happening is that prior to
the above being run, we've switched user over to the owner of the table
to perform this check.

This script illustrates what I'm talking about:

CREATE USER pk_owner;
CREATE USER fk_owner;
GRANT pk_owner TO sfrost;
GRANT fk_owner TO sfrost;

SET ROLE pk_owner;
CREATE TABLE pk_tbl (a int primary key, b text);
INSERT INTO pk_tbl VALUES (1,'aaa'), (2,'bbb'), (3,'ccc');
GRANT REFERENCES ON pk_tbl TO fk_owner;

SET ROLE fk_owner;
CREATE TABLE fk_tbl (x int, y text);
INSERT INTO fk_tbl VALUES (1,'xxx'), (2,'yyy'), (3,'zzz');

ALTER TABLE fk_tbl ADD FOREIGN KEY (x) REFERENCES pk_tbl (a);

ALTER TABLE fk_tbl DROP CONSTRAINT fk_tbl_x_fkey;

SET ROLE pk_owner;
REVOKE ALL ON pk_tbl FROM pk_owner;

SET ROLE fk_owner;
ALTER TABLE fk_tbl ADD FOREIGN KEY (x) REFERENCES pk_tbl (a);

ERROR:  permission denied for relation pk_tbl
CONTEXT:  SQL statement "SELECT 1 FROM ONLY "public"."pk_tbl" x WHERE
"a" OPERATOR(pg_catalog.=) $1 FOR SHARE OF x"

This does make me think (as I've thought in the past..) that we really
should say *who* doesn't have that permission.  We run into the same
problem with views- they're run as the owner of the view, so you can get
a permission denied error trying to select from the view when you
clearly have select rights on the view itself.

As it turns out, the check in RI_Initial_Check() to provide the speed-up
is if the current role can just SELECT against the PK table- in which
case, you can run the check as the FK user and not have to change user.
We can't just switch to the PK user and run the same query though,
because that user might not have any rights on the FK table.  So, we end
up taking the slow path, which fires off the FK trigger that's been set
up on the fk_tbl but which runs as the owner of the pk_tbl.

So, long-and-short, I don't see that we have a bug in any of this.  I do
think we should allow RI_Initial_Check() to run if it has the necessary
column-level permissions, and we should remove the duplicate
permissions-checking code in favor of using the same code the executor
will, which happens to also be where the new hook we're talking about
is.
Thanks,
    Stephen

pgsql-hackers by date:

Previous
From: Simon Riggs
Date:
Subject: Re: Synchronization levels in SR
Next
From: Giles Lean
Date:
Subject: Re: libpq, PQexecPrepared, data size sent to FE vs. FETCH_COUNT