'updable view can update the base table, but cannot update view column

BEGIN;
SET search_path TO test;
CREATE USER regress_view_user1;
CREATE USER regress_view_user2;
GRANT CREATE, usage ON SCHEMA test TO regress_view_user1;
GRANT CREATE, usage ON SCHEMA test TO regress_view_user2;
SET session AUTHORIZATION regress_view_user1;
CREATE TABLE test.base_tbl (
    a int UNIQUE,
    b text,
    c float
);
INSERT INTO test.base_tbl
    VALUES (1, 'xxx', 1.0);
GRANT SELECT (a, b) ON test.base_tbl TO regress_view_user2;
GRANT INSERT (a, b) ON test.base_tbl TO regress_view_user2;
GRANT UPDATE (a, b) ON test.base_tbl TO regress_view_user2;
COMMIT;

SET session AUTHORIZATION regress_view_user2;

CREATE VIEW rw_view2 AS
SELECT
    b AS bb,
    c AS cc,
    a AS aa
FROM
    base_tbl;

INSERT INTO rw_view2 (aa, bb)
    VALUES (1, 'xxx')
ON CONFLICT (aa)
    DO UPDATE SET
        bb = excluded.bb;-- Not allowed

ERROR: 42501: permission denied for table base_tbl I am not sure why this would be permission denied.

select aa, bb from rw_view2;--permission denied
 select a,b from base_tbl; --permission ok.


Solution 1:[1]

Edit: Rewrote this with more detail and a more helpful solution

Views are implemented in Postgres as Select rewrite rules on an empty table which run the SELECT statement that you use to define the view. These rewrite rules operate with the same permissions as the owner of the newly created table.

When you execute

select aa, bb from rw_view2

you are likely expecting that the query planner is going to recognize that column cc from rw_view2 isn't needed and therefore a lack of read permissions on column c is irrelevant -and indeed the query planner will actually execute a table scan on base_tbl; however, Postgres going to check permissions BEFORE the query planner does optimization. The rewrite rule is going generate a parse tree equivalent to:

SELECT aa,bb
FROM (
  SELECT
      b AS bb,
      c AS cc,
      a AS aa
  FROM
      test.base_tbl
) as rw_view2

which regress_user2 does not have the permissions to execute, resulting in the error you experience.

Similary

UPDATE test.rw_view2 SET bb = 'xxx' WHERE aa = 1;

fails because what the query planner sees, for purposes of permissions, is something like:

UPDATE test.base_tbl SET b = 'xxx'
FROM (
 SELECT
     b AS bb,
     c AS cc,
     a AS aa
 FROM
     test.base_tbl
) as rw_view2

WHERE base_tbl.a = rw_view2.aa;

You wouldn't want the authorization of operations to succeed or fail based on optimizations the query planner uses -as this would lead to inconsistent behavior that produces difficult-to-debug errors when executing statements.

So fundamentally a user needs SELECT permissions on all the columns that they define in their view for any non trivial INSERT/UPDATE operations.

However, you can work around this restriction by creating the view under regress_user1 and then granting SELECT,UPDATE,INSERT permissions to regress_view_user2. This setup allows you to directly define the permissions you want regress_view_user2 to have on rw_view2 instead of base_tbl:

DROP SCHEMA IF EXISTS test;
CREATE SCHEMA test;
CREATE USER regress_view_user1;
CREATE USER regress_view_user2;
GRANT CREATE, usage ON SCHEMA test TO regress_view_user1;
GRANT CREATE, usage ON SCHEMA test TO regress_view_user2;
SET session AUTHORIZATION regress_view_user1;
CREATE TABLE test.base_tbl (
   a int UNIQUE,
   b text,
   c float
);
INSERT INTO test.base_tbl
VALUES (1, 'xxx', 1.0);

CREATE VIEW test.rw_view2 AS
SELECT
    b AS bb,
    c AS cc,
    a AS aa
FROM
    test.base_tbl;

GRANT SELECT (aa,bb) ON test.rw_view2 TO regress_view_user2;
GRANT INSERT (aa,bb) ON test.rw_view2 TO regress_view_user2;
GRANT UPDATE (aa,bb) ON test.rw_view2 TO regress_view_user2;

SET session authorization regress_view_user2;

INSERT INTO test.rw_view2 (aa, bb)
VALUES (1, 'xxx')
ON CONFLICT (aa)
    DO UPDATE SET
    bb = excluded.bb; --succeeds

In this case regress_view_user1's SELECT permissions on base_tbl allow the view to be computed, and since regress_user2 is only using columns from the view they have been specifically granted access to, the INSERT ON CONFLICT operation is allowed to proceed.

If your view has a WHERE clause, there are some additional security considerations you might want to take into account when defining views such as using security_barriers and also setting the appropriate CHECK OPTION on the view for INSERTS/UPDATES. More info on using rules here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1