Fix column privilege checking for cases where parent and child have different
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2009 17:30:29 +0000 (17:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2009 17:30:29 +0000 (17:30 +0000)
attribute numbering.  Also, a parent whole-row reference should not require
select privilege on child columns that aren't inherited from the parent.
Problem diagnosed by KaiGai Kohei, though this isn't exactly his patch.

src/backend/optimizer/prep/prepunion.c
src/test/regress/expected/privileges.out
src/test/regress/sql/privileges.sql

index 13d08eef3eec666f984f6bc310d49bb24bcfb59e..43aa515ae5efa404d133c1c41fb6df69587ca7ba 100644 (file)
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.166 2009/02/25 03:30:37 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.167 2009/03/05 17:30:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 
 
 #include "access/heapam.h"
+#include "access/sysattr.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "miscadmin.h"
@@ -95,6 +96,8 @@ static void make_inh_translation_list(Relation oldrelation,
                          Relation newrelation,
                          Index newvarno,
                          List **translated_vars);
+static Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
+                                     List *translated_vars);
 static Node *adjust_appendrel_attrs_mutator(Node *node,
                               AppendRelInfo *context);
 static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid);
@@ -1295,6 +1298,19 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti)
        appinfo->parent_reloid = parentOID;
        appinfos = lappend(appinfos, appinfo);
 
+       /*
+        * Translate the column permissions bitmaps to the child's attnums
+        * (we have to build the translated_vars list before we can do this).
+        * But if this is the parent table, leave copyObject's result alone.
+        */
+       if (childOID != parentOID)
+       {
+           childrte->selectedCols = translate_col_privs(rte->selectedCols,
+                                                        appinfo->translated_vars);
+           childrte->modifiedCols = translate_col_privs(rte->modifiedCols,
+                                                        appinfo->translated_vars);
+       }
+
        /*
         * Build a RowMarkClause if parent is marked FOR UPDATE/SHARE.
         */
@@ -1437,6 +1453,59 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation,
    *translated_vars = vars;
 }
 
+/*
+ * translate_col_privs
+ *   Translate a bitmapset representing per-column privileges from the
+ *   parent rel's attribute numbering to the child's.
+ *
+ * The only surprise here is that we don't translate a parent whole-row
+ * reference into a child whole-row reference.  That would mean requiring
+ * permissions on all child columns, which is overly strict, since the
+ * query is really only going to reference the inherited columns.  Instead
+ * we set the per-column bits for all inherited columns.
+ */
+static Bitmapset *
+translate_col_privs(const Bitmapset *parent_privs,
+                   List *translated_vars)
+{
+   Bitmapset  *child_privs = NULL;
+   bool        whole_row;
+   int         attno;
+   ListCell   *lc;
+
+   /* System attributes have the same numbers in all tables */
+   for (attno = FirstLowInvalidHeapAttributeNumber+1; attno < 0; attno++)
+   {
+       if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
+                         parent_privs))
+           child_privs = bms_add_member(child_privs,
+                                        attno - FirstLowInvalidHeapAttributeNumber);
+   }
+
+   /* Check if parent has whole-row reference */
+   whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber,
+                             parent_privs);
+
+   /* And now translate the regular user attributes, using the vars list */
+   attno = InvalidAttrNumber;
+   foreach(lc, translated_vars)
+   {
+       Var    *var = (Var *) lfirst(lc);
+
+       attno++;
+       if (var == NULL)        /* ignore dropped columns */
+           continue;
+       Assert(IsA(var, Var));
+       if (whole_row ||
+           bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
+                         parent_privs))
+           child_privs = bms_add_member(child_privs,
+                                        var->varattno - FirstLowInvalidHeapAttributeNumber);
+   }
+
+   return child_privs;
+}
+
 /*
  * adjust_appendrel_attrs
  *   Copy the specified query or expression and translate Vars referring
index 8129f5b915b0e3d8c8a6bbd91f5d72516ae98f11..a17ff59d0c81182b11f094c21ec7c1ef00cc55e0 100644 (file)
@@ -393,6 +393,48 @@ SET SESSION AUTHORIZATION regressuser3;
 DELETE FROM atest5 WHERE one = 1; -- fail
 ERROR:  permission denied for relation atest5
 DELETE FROM atest5 WHERE two = 2; -- ok
+-- check inheritance cases
+SET SESSION AUTHORIZATION regressuser1;
+CREATE TABLE atestp1 (f1 int, f2 int) WITH OIDS;
+CREATE TABLE atestp2 (fx int, fy int) WITH OIDS;
+CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2);
+GRANT SELECT(fx,fy,oid) ON atestp2 TO regressuser2;
+GRANT SELECT(fx) ON atestc TO regressuser2;
+SET SESSION AUTHORIZATION regressuser2;
+SELECT fx FROM atestp2; -- ok
+ fx 
+----
+(0 rows)
+
+SELECT fy FROM atestp2; -- fail, no privilege on atestc.fy
+ERROR:  permission denied for relation atestc
+SELECT atestp2 FROM atestp2; -- fail, no privilege on atestc.fy
+ERROR:  permission denied for relation atestc
+SELECT oid FROM atestp2; -- fail, no privilege on atestc.oid
+ERROR:  permission denied for relation atestc
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT(fy,oid) ON atestc TO regressuser2;
+SET SESSION AUTHORIZATION regressuser2;
+SELECT fx FROM atestp2; -- still ok
+ fx 
+----
+(0 rows)
+
+SELECT fy FROM atestp2; -- ok
+ fy 
+----
+(0 rows)
+
+SELECT atestp2 FROM atestp2; -- ok
+ atestp2 
+---------
+(0 rows)
+
+SELECT oid FROM atestp2; -- ok
+ oid 
+-----
+(0 rows)
+
 -- privileges on functions, languages
 -- switch to superuser
 \c -
@@ -791,6 +833,9 @@ DROP TABLE atest3;
 DROP TABLE atest4;
 DROP TABLE atest5;
 DROP TABLE atest6;
+DROP TABLE atestc;
+DROP TABLE atestp1;
+DROP TABLE atestp2;
 DROP GROUP regressgroup1;
 DROP GROUP regressgroup2;
 REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
index 2316d1161628fe11d920ed47fe8d3986d28eda70..5aa1012f3f619de7befd2d2222874d6a6c938a7e 100644 (file)
@@ -267,6 +267,29 @@ SET SESSION AUTHORIZATION regressuser3;
 DELETE FROM atest5 WHERE one = 1; -- fail
 DELETE FROM atest5 WHERE two = 2; -- ok
 
+-- check inheritance cases
+SET SESSION AUTHORIZATION regressuser1;
+CREATE TABLE atestp1 (f1 int, f2 int) WITH OIDS;
+CREATE TABLE atestp2 (fx int, fy int) WITH OIDS;
+CREATE TABLE atestc (fz int) INHERITS (atestp1, atestp2);
+GRANT SELECT(fx,fy,oid) ON atestp2 TO regressuser2;
+GRANT SELECT(fx) ON atestc TO regressuser2;
+
+SET SESSION AUTHORIZATION regressuser2;
+SELECT fx FROM atestp2; -- ok
+SELECT fy FROM atestp2; -- fail, no privilege on atestc.fy
+SELECT atestp2 FROM atestp2; -- fail, no privilege on atestc.fy
+SELECT oid FROM atestp2; -- fail, no privilege on atestc.oid
+
+SET SESSION AUTHORIZATION regressuser1;
+GRANT SELECT(fy,oid) ON atestc TO regressuser2;
+
+SET SESSION AUTHORIZATION regressuser2;
+SELECT fx FROM atestp2; -- still ok
+SELECT fy FROM atestp2; -- ok
+SELECT atestp2 FROM atestp2; -- ok
+SELECT oid FROM atestp2; -- ok
+
 -- privileges on functions, languages
 
 -- switch to superuser
@@ -466,6 +489,9 @@ DROP TABLE atest3;
 DROP TABLE atest4;
 DROP TABLE atest5;
 DROP TABLE atest6;
+DROP TABLE atestc;
+DROP TABLE atestp1;
+DROP TABLE atestp2;
 
 DROP GROUP regressgroup1;
 DROP GROUP regressgroup2;