Fix changing the ownership of ALL TABLES IN SCHEMA publication.
authorAmit Kapila <akapila@postgresql.org>
Wed, 8 Dec 2021 06:01:16 +0000 (11:31 +0530)
committerAmit Kapila <akapila@postgresql.org>
Wed, 8 Dec 2021 06:01:16 +0000 (11:31 +0530)
Ensure that the new owner of ALL TABLES IN SCHEMA publication must be a
superuser. The same is already ensured during CREATE PUBLICATION.

Author: Vignesh C
Reviewed-by: Nathan Bossart, Greg Nancarrow, Michael Paquier, Haiying Tang
Discussion: https://postgr.es/m/CALDaNm0E5U-RqxFuFrkZrQeG7ae5trGa=xs=iRtPPHULtT4zOw@mail.gmail.com

src/backend/catalog/pg_publication.c
src/backend/commands/publicationcmds.c
src/include/catalog/pg_publication.h
src/test/regress/expected/publication.out
src/test/regress/sql/publication.sql

index b40293fcb31ae031e3923d952e498e7ec67c4b19..65db07f60243d2d917079f1391c81e193323bc2b 100644 (file)
@@ -193,6 +193,36 @@ is_publishable_relation(Relation rel)
        return is_publishable_class(RelationGetRelid(rel), rel->rd_rel);
 }
 
+/*
+ * Returns true if any schema is associated with the publication, false if no
+ * schema is associated with the publication.
+ */
+bool
+is_schema_publication(Oid pubid)
+{
+       Relation        pubschsrel;
+       ScanKeyData scankey;
+       SysScanDesc scan;
+       HeapTuple       tup;
+       bool            result = false;
+
+       pubschsrel = table_open(PublicationNamespaceRelationId, AccessShareLock);
+       ScanKeyInit(&scankey,
+                               Anum_pg_publication_namespace_pnpubid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(pubid));
+
+       scan = systable_beginscan(pubschsrel,
+                                                         PublicationNamespacePnnspidPnpubidIndexId,
+                                                         true, NULL, 1, &scankey);
+       tup = systable_getnext(scan);
+       result = HeapTupleIsValid(tup);
+
+       systable_endscan(scan);
+       table_close(pubschsrel, AccessShareLock);
+
+       return result;
+}
 
 /*
  * SQL-callable variant of the above
index 7d4a0e95f6cb58998706b29a314510a4b06e3186..404bb5d0c875bf73fb56ee865d4a4c7353aad280 100644 (file)
@@ -1192,6 +1192,13 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
                                         errmsg("permission denied to change owner of publication \"%s\"",
                                                        NameStr(form->pubname)),
                                         errhint("The owner of a FOR ALL TABLES publication must be a superuser.")));
+
+               if (!superuser_arg(newOwnerId) && is_schema_publication(form->oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                                        errmsg("permission denied to change owner of publication \"%s\"",
+                                                       NameStr(form->pubname)),
+                                        errhint("The owner of a FOR ALL TABLES IN SCHEMA publication must be a superuser.")));
        }
 
        form->pubowner = newOwnerId;
index 1ae439e6f36cb924f411b84fe258e6b2b2798405..902f2f2f0dafff4ac38c262f153152335f680c10 100644 (file)
@@ -122,6 +122,7 @@ extern List *GetPubPartitionOptionRelations(List *result,
                                                                                        Oid relid);
 
 extern bool is_publishable_relation(Relation rel);
+extern bool is_schema_publication(Oid pubid);
 extern ObjectAddress publication_add_relation(Oid pubid, PublicationRelInfo *targetrel,
                                                                                          bool if_not_exists);
 extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
index a2115c1c6060d75e492bc71c3ea216b94490d05d..c096fbdac58f928077c1183933107c4636d290fd 100644 (file)
@@ -373,6 +373,21 @@ ALTER PUBLICATION testpub2 ADD TABLE testpub_tbl1;  -- ok
 DROP PUBLICATION testpub2;
 DROP PUBLICATION testpub3;
 SET ROLE regress_publication_user;
+CREATE ROLE regress_publication_user3;
+GRANT regress_publication_user2 TO regress_publication_user3;
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub4 FOR ALL TABLES IN SCHEMA pub_test;
+RESET client_min_messages;
+ALTER PUBLICATION testpub4 OWNER TO regress_publication_user3;
+SET ROLE regress_publication_user3;
+-- fail - new owner must be superuser
+ALTER PUBLICATION testpub4 owner to regress_publication_user2; -- fail
+ERROR:  permission denied to change owner of publication "testpub4"
+HINT:  The owner of a FOR ALL TABLES IN SCHEMA publication must be a superuser.
+ALTER PUBLICATION testpub4 owner to regress_publication_user; -- ok
+SET ROLE regress_publication_user;
+DROP PUBLICATION testpub4;
+DROP ROLE regress_publication_user3;
 REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
 DROP TABLE testpub_parted;
 DROP TABLE testpub_tbl1;
index 2fe41b07ae2ab95704a903ad40d81b5043fd36db..06628825444568b0cd738506784300f075ac2098 100644 (file)
@@ -218,6 +218,21 @@ DROP PUBLICATION testpub2;
 DROP PUBLICATION testpub3;
 
 SET ROLE regress_publication_user;
+CREATE ROLE regress_publication_user3;
+GRANT regress_publication_user2 TO regress_publication_user3;
+SET client_min_messages = 'ERROR';
+CREATE PUBLICATION testpub4 FOR ALL TABLES IN SCHEMA pub_test;
+RESET client_min_messages;
+ALTER PUBLICATION testpub4 OWNER TO regress_publication_user3;
+SET ROLE regress_publication_user3;
+-- fail - new owner must be superuser
+ALTER PUBLICATION testpub4 owner to regress_publication_user2; -- fail
+ALTER PUBLICATION testpub4 owner to regress_publication_user; -- ok
+
+SET ROLE regress_publication_user;
+DROP PUBLICATION testpub4;
+DROP ROLE regress_publication_user3;
+
 REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
 
 DROP TABLE testpub_parted;