Update per-column ACLs, not only per-table ACL, when changing table owner.
authorTom Lane
Wed, 21 Dec 2011 23:23:24 +0000 (18:23 -0500)
committerTom Lane
Wed, 21 Dec 2011 23:23:24 +0000 (18:23 -0500)
We forgot to modify column ACLs, so privileges were still shown as having
been granted by the old owner.  This meant that neither the new owner nor
a superuser could revoke the now-untraceable-to-table-owner permissions.
Per bug #6350 from Marc Balmer.

This has been wrong since column ACLs were added, so back-patch to 8.4.

src/backend/commands/tablecmds.c

index 85fc2793b872cacd5df4bd78cf65adc635c1587c..12c94e199306c373328d0e6c5d53ad2990dafa69 100644 (file)
@@ -316,6 +316,8 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                      const char *colName, TypeName *typeName);
 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab);
 static void ATPostAlterTypeParse(char *cmd, List **wqueue);
+static void change_owner_fix_column_acls(Oid relationOid,
+                            Oid oldOwnerId, Oid newOwnerId);
 static void change_owner_recurse_to_sequences(Oid relationOid,
                                  Oid newOwnerId);
 static void ATExecClusterOn(Relation rel, const char *indexName);
@@ -6644,6 +6646,14 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
 
        heap_freetuple(newtuple);
 
+       /*
+        * We must similarly update any per-column ACLs to reflect the new
+        * owner; for neatness reasons that's split out as a subroutine.
+        */
+       change_owner_fix_column_acls(relationOid,
+                                    tuple_class->relowner,
+                                    newOwnerId);
+
        /*
         * Update owner dependency reference, if any.  A composite type has
         * none, because it's tracked for the pg_type entry instead of here;
@@ -6700,6 +6710,71 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
    relation_close(target_rel, NoLock);
 }
 
+/*
+ * change_owner_fix_column_acls
+ *
+ * Helper function for ATExecChangeOwner.  Scan the columns of the table
+ * and fix any non-null column ACLs to reflect the new owner.
+ */
+static void
+change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
+{
+   Relation    attRelation;
+   SysScanDesc scan;
+   ScanKeyData key[1];
+   HeapTuple   attributeTuple;
+
+   attRelation = heap_open(AttributeRelationId, RowExclusiveLock);
+   ScanKeyInit(&key[0],
+               Anum_pg_attribute_attrelid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(relationOid));
+   scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
+                             true, SnapshotNow, 1, key);
+   while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
+   {
+       Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
+       Datum       repl_val[Natts_pg_attribute];
+       bool        repl_null[Natts_pg_attribute];
+       bool        repl_repl[Natts_pg_attribute];
+       Acl        *newAcl;
+       Datum       aclDatum;
+       bool        isNull;
+       HeapTuple   newtuple;
+
+       /* Ignore dropped columns */
+       if (att->attisdropped)
+           continue;
+
+       aclDatum = heap_getattr(attributeTuple,
+                               Anum_pg_attribute_attacl,
+                               RelationGetDescr(attRelation),
+                               &isNull);
+       /* Null ACLs do not require changes */
+       if (isNull)
+           continue;
+
+       memset(repl_null, false, sizeof(repl_null));
+       memset(repl_repl, false, sizeof(repl_repl));
+
+       newAcl = aclnewowner(DatumGetAclP(aclDatum),
+                            oldOwnerId, newOwnerId);
+       repl_repl[Anum_pg_attribute_attacl - 1] = true;
+       repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
+
+       newtuple = heap_modify_tuple(attributeTuple,
+                                    RelationGetDescr(attRelation),
+                                    repl_val, repl_null, repl_repl);
+
+       simple_heap_update(attRelation, &newtuple->t_self, newtuple);
+       CatalogUpdateIndexes(attRelation, newtuple);
+
+       heap_freetuple(newtuple);
+   }
+   systable_endscan(scan);
+   heap_close(attRelation, RowExclusiveLock);
+}
+
 /*
  * change_owner_recurse_to_sequences
  *