diff --git a/doc/src/sgml/ref/alter_publication.sgml b/doc/src/sgml/ref/alter_publication.sgml index 47d83b80be5..776661bbeb3 100644 --- a/doc/src/sgml/ref/alter_publication.sgml +++ b/doc/src/sgml/ref/alter_publication.sgml @@ -48,8 +48,11 @@ ALTER PUBLICATION name DROP TABLE < - To alter the owner, you must also be a direct or indirect member of the - new owning role. The new owner has to be a superuser + To alter the owner, you must also be a direct or indirect member of the new + owning role. The new owner must have CREATE privilege on + the database. Also, the new owner of a FOR ALL TABLES + publication must be a superuser. However, a superuser can change the + ownership of a publication while circumventing these restrictions. diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 04f83e0a2ea..d69e39dc5b4 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -670,17 +670,31 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) if (form->pubowner == newOwnerId) return; - if (!pg_publication_ownercheck(HeapTupleGetOid(tup), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PUBLICATION, - NameStr(form->pubname)); + if (!superuser()) + { + AclResult aclresult; - /* New owner must be a superuser */ - if (!superuser_arg(newOwnerId)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to change owner of publication \"%s\"", - NameStr(form->pubname)), - errhint("The owner of a publication must be a superuser."))); + /* Must be owner */ + if (!pg_publication_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PUBLICATION, + NameStr(form->pubname)); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* New owner must have CREATE privilege on database */ + aclresult = pg_database_aclcheck(MyDatabaseId, newOwnerId, ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + + if (form->puballtables && !superuser_arg(newOwnerId)) + 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 publication must be a superuser."))); + } form->pubowner = newOwnerId; CatalogTupleUpdate(rel, &tup->t_self, tup); diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 7c4834b213c..5a7c0edf7d5 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -182,6 +182,14 @@ ALTER PUBLICATION testpub_default RENAME TO testpub_foo; -- rename back to keep the rest simple ALTER PUBLICATION testpub_foo RENAME TO testpub_default; +ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; +\dRp testpub_default + List of publications + Name | Owner | Inserts | Updates | Deletes +-----------------+---------------------------+---------+---------+--------- + testpub_default | regress_publication_user2 | t | t | t +(1 row) + DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl; diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 46d275acc59..cff9931a77f 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -108,6 +108,10 @@ ALTER PUBLICATION testpub_default RENAME TO testpub_foo; -- rename back to keep the rest simple ALTER PUBLICATION testpub_foo RENAME TO testpub_default; +ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2; + +\dRp testpub_default + DROP PUBLICATION testpub_default; DROP PUBLICATION testpib_ins_trunct; DROP PUBLICATION testpub_fortbl;