Re: New patch for Column-level privileges - Mailing list pgsql-hackers

From KaiGai Kohei
Subject Re: New patch for Column-level privileges
Date
Msg-id 49644FAB.7060602@ak.jp.nec.com
Whole thread Raw
In response to Re: New patch for Column-level privileges  (KaiGai Kohei <kaigai@ak.jp.nec.com>)
Responses Re: New patch for Column-level privileges  (Markus Wanner <markus@bluegap.ch>)
Re: New patch for Column-level privileges  (Stephen Frost <sfrost@snowman.net>)
Re: New patch for Column-level privileges  ("Jaime Casanova" <jcasanov@systemguards.com.ec>)
List pgsql-hackers
>>> Currently,
>>> column-level privileges are not honored when JOINs are involved (you
>>> must have the necessary table-level privileges, as you do today). It
>>> would really be great to have that working and mainly involves
>>> modifying the rewriter to add on to the appropriate range table column
>>> list entries the columns which are used in the joins and output from
>>> joins.
>>
>> Understood. Do you plan to work on that? Or do you think the patch
>> should go into 8.4 even without support for JOINs?
>
> I tried to check the latest Column-level privileges patch.
>
> This trouble related to JOINs is come from inappropriate handling
> of rte->cols_sel list, in my opinion.
> The list is constructed at scanRTEForColumn() and expandRelation().
> However, scanRTEForColumn() appends an appeared attribute number
> even if given RangeTblEntry is RTE_JOIN, and expandRelation() appends
> all the attribute number contained within the given relation.
>
> I think your design is basically fine, so the issue is minor one.
> But it is necessary to pick up carefully what columns are really
> accessed.
>
> Here is one proposition.
> Is it possible to implement a walker function to pick up appeared
> columns and to chain them on rte->cols_sel/cols_mod?
> In this idea, columns in Query->targetList should be chained on
> rte->cols_mod, and others should be chained on rte->cols_sel.
>
> Here is an example to pick up all appeared relation:
> http://code.google.com/p/sepgsql/source/browse/trunk/sepgsql/src/backend/security/rowacl/rowacl.c#30
>
>
> If you don't have enough availability, I'll be able to do it within
> a few days.

The attached patch is a proof of the concept.
It walks on a given query tree to append accessed columns on
rte->cols_sel and rte->cols_mod.
When aliasvar of JOIN'ed relation is accesses, its source is
appended on the list.

This patch can be applied on the latest CVS HEAD with Stephen's
colprivs_wip.2009010201.diff.gz.

Any comment?

I strongly want the Column-level privileges to be get merged
as soon as possible, so I don't spare any possible assist
for his works.

Thanks,

---- example of the patched Column-level privileges ----
postgres=# CREATE TABLE t1 (a int, b text, c bool);
CREATE TABLE
postgres=# CREATE TABLE t2 (x int, y text, z bool);
CREATE TABLE
postgres=# GRANT SELECT(a,b) ON t1 TO ymj;
GRANT
postgres=# GRANT UPDATE(a,b) ON t1 TO ymj;
GRANT
postgres=# GRANT SELECT(x,y) ON t2 TO ymj;
GRANT
postgres=# INSERT INTO t1 VALUES (1, 'aaa', true), (2, 'bbb', null), (3, 'ccc', false);
INSERT 0 3
postgres=# INSERT INTO t2 VALUES (1, 'xxx', false), (2, 'yyy', null), (3, 'zzz', true);
INSERT 0 3
postgres=# \c - ymj
psql (8.4devel)
You are now connected to database "postgres" as user "ymj".
postgres=> SELECT * FROM t1;
NOTICE:  pg_attribute_aclmask: t1.a required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t1.b required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t1.c required: 0002 allowed: 0000
ERROR:  permission denied for relation t1
postgres=> SELECT a,b FROM t1;
NOTICE:  pg_attribute_aclmask: t1.a required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t1.b required: 0002 allowed: 0002
  a |  b
---+-----
  1 | aaa
  2 | bbb
  3 | ccc
(3 rows)

postgres=> SELECT x,y FROM t2;
NOTICE:  pg_attribute_aclmask: t2.x required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t2.y required: 0002 allowed: 0002
  x |  y
---+-----
  1 | xxx
  2 | yyy
  3 | zzz
(3 rows)

postgres=> SELECT b,y FROM t1 JOIN t2 ON a = x;
NOTICE:  pg_attribute_aclmask: t1.b required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t1.a required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t2.y required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t2.x required: 0002 allowed: 0002
   b  |  y
-----+-----
  aaa | xxx
  bbb | yyy
  ccc | zzz
(3 rows)

postgres=> SELECT b,z FROM t1 JOIN t2 ON a = x;
NOTICE:  pg_attribute_aclmask: t1.b required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t1.a required: 0002 allowed: 0002
NOTICE:  pg_attribute_aclmask: t2.z required: 0002 allowed: 0000
ERROR:  permission denied for relation t2
postgres=> UPDATE t1 SET a = 1;
NOTICE:  pg_attribute_aclmask: t1.a required: 0004 allowed: 0004
UPDATE 3
postgres=> UPDATE t1 SET c = true;
NOTICE:  pg_attribute_aclmask: t1.c required: 0004 allowed: 0000
ERROR:  permission denied for relation t1
postgres=>

--
OSS Platform Development Division, NEC
KaiGai Kohei <kaigai@ak.jp.nec.com>
Index: src/backend/parser/analyze.c
===================================================================
*** src/backend/parser/analyze.c    (revision 1)
--- src/backend/parser/analyze.c    (working copy)
***************
*** 27,32 ****
--- 27,33 ----
  #include "catalog/pg_type.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
+ #include "optimizer/tlist.h"
  #include "optimizer/var.h"
  #include "parser/analyze.h"
  #include "parser/parse_agg.h"
***************
*** 267,272 ****
--- 268,338 ----
  }

  /*
+  * applyColumnPrivs()
+  *     construct rte->cols_sel and rte->cols_mod for Column-level
+  *     privileges.
+  */
+ static bool
+ applyColumnPrivsWalker(Node *node, ParseState *pstate)
+ {
+     if (!node)
+         return false;
+
+     if (IsA(node, Var))
+     {
+         RangeTblEntry *rte;
+         Var *v = (Var *) node;
+         int lv;
+
+         for (lv = v->varlevelsup; lv > 0; lv--)
+         {
+             Assert(pstate->parentParseState != NULL);
+             pstate = pstate->parentParseState;
+         }
+
+         rte = rt_fetch(v->varno, pstate->p_rtable);
+         Assert(IsA(rte, RangeTblEntry));
+
+         if (rte->rtekind == RTE_RELATION)
+         {
+             rte->cols_sel = lappend_int(rte->cols_sel, v->varattno);
+         }
+         else if (rte->rtekind == RTE_JOIN)
+         {
+             node = list_nth(rte->joinaliasvars, v->varattno - 1);
+
+             applyColumnPrivsWalker(node, pstate);
+         }
+     }
+     else if (IsA(node, SortGroupClause))
+         return false;
+
+     return expression_tree_walker(node, applyColumnPrivsWalker, (void *) pstate);
+ }
+
+ static void
+ applyColumnPrivs(Query *query, ParseState *pstate)
+ {
+     ListCell *l;
+
+     if (query->commandType != CMD_SELECT)
+     {
+         RangeTblEntry *rte
+             = rt_fetch(query->resultRelation, query->rtable);
+
+         foreach (l, query->targetList)
+         {
+             TargetEntry *tle = lfirst(l);
+
+             Assert(IsA(tle, TargetEntry));
+             rte->cols_mod = lappend_int(rte->cols_mod, tle->resno);
+         }
+     }
+     query_tree_walker(query, applyColumnPrivsWalker, (void *) pstate,
+                       QTW_IGNORE_JOINALIASES);
+ }
+
+ /*
   * transformDeleteStmt -
   *      transforms a Delete Statement
   */
***************
*** 683,688 ****
--- 749,756 ----
                   parser_errposition(pstate,
                                      locate_windowfunc((Node *) qry))));

+     applyColumnPrivs(qry, pstate);
+
      return qry;
  }

***************
*** 876,881 ****
--- 944,951 ----
          transformLockingClause(pstate, qry, (LockingClause *) lfirst(l));
      }

+     applyColumnPrivs(qry, pstate);
+
      return qry;
  }

***************
*** 1716,1721 ****
--- 1786,1793 ----
      if (origTargetList != NULL)
          elog(ERROR, "UPDATE target count mismatch --- internal error");

+     applyColumnPrivs(qry, pstate);
+
      return qry;
  }

Index: src/backend/parser/parse_relation.c
===================================================================
*** src/backend/parser/parse_relation.c    (revision 2)
--- src/backend/parser/parse_relation.c    (working copy)
***************
*** 479,485 ****
              result = (Node *) make_var(pstate, rte, attnum, location);
              /* Require read access */
              rte->requiredPerms |= ACL_SELECT;
-             rte->cols_sel = lappend_int(rte->cols_sel,attnum);
          }
      }

--- 479,484 ----
***************
*** 508,514 ****
                  result = (Node *) make_var(pstate, rte, attnum, location);
                  /* Require read access */
                  rte->requiredPerms |= ACL_SELECT;
-                 rte->cols_sel = lappend_int(rte->cols_sel,attnum);
              }
          }
      }
--- 507,512 ----
***************
*** 1749,1762 ****
      expandTupleDesc(rel->rd_att, rte->eref, rtindex, sublevels_up,
                      location, include_dropped,
                      colnames, colvars);
-
-     for (attrno = 0; attrno < rel->rd_att->natts; attrno++)
-     {
-         Form_pg_attribute attr = rel->rd_att->attrs[attrno];
-
-         if (!attr->attisdropped)
-             rte->cols_sel = lappend_int(rte->cols_sel, attrno+1);
-     }
      relation_close(rel, AccessShareLock);
  }

--- 1747,1752 ----
Index: src/backend/parser/parse_target.c
===================================================================
*** src/backend/parser/parse_target.c    (revision 2)
--- src/backend/parser/parse_target.c    (working copy)
***************
*** 372,378 ****
      attrtype = attnumTypeId(rd, attrno);
      attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;

-     pstate->p_target_rangetblentry->cols_mod = lappend_int(pstate->p_target_rangetblentry->cols_mod, attrno);

      /*
       * If the expression is a DEFAULT placeholder, insert the attribute's
--- 372,377 ----
***************
*** 785,791 ****
              col->location = -1;
              cols = lappend(cols, col);
              *attrnos = lappend_int(*attrnos, i + 1);
-             pstate->p_target_rangetblentry->cols_mod = lappend_int(pstate->p_target_rangetblentry->cols_mod, i + 1);
          }
      }
      else
--- 784,789 ----
***************
*** 842,848 ****
              }

              *attrnos = lappend_int(*attrnos, attrno);
-             pstate->p_target_rangetblentry->cols_mod = lappend_int(pstate->p_target_rangetblentry->cols_mod, attrno);
          }
      }

--- 840,845 ----
Index: src/backend/catalog/aclchk.c
===================================================================
*** src/backend/catalog/aclchk.c    (revision 2)
--- src/backend/catalog/aclchk.c    (working copy)
***************
*** 2291,2296 ****
--- 2291,2302 ----

      pfree(acl);

+     elog(NOTICE, "%s: %s.%s required: %04x allowed: %04x",
+          __FUNCTION__,
+          NameStr(classForm->relname),
+          NameStr(((Form_pg_attribute) GETSTRUCT(attTuple))->attname),
+          mask, result);
+
      /* if we have a detoasted copy, free it */
      if (colacl && (Pointer) colacl != DatumGetPointer(colaclDatum))
          pfree(colacl);

pgsql-hackers by date:

Previous
From: Greg Smith
Date:
Subject: Re: version() output vs. 32/64 bits
Next
From: Heikki Linnakangas
Date:
Subject: Re: Visibility map and freezing