From: Noah Misch <noah@leadboat.com>
To: pgsql-hackers@postgresql.org
Subject: Dump public schema ownership & seclabels
Date: Tue, 29 Dec 2020 05:49:24 -0800
Message-ID: <20201229134924.GA1431748@rfd.leadboat.com> (raw)
https://postgr.es/m/20201031163518.GB4039133@rfd.leadboat.com gave $SUBJECT as
one of the constituent projects for changing the public schema default ACL.
This ended up being simple. Attached. I chose to omit the "ALTER SCHEMA
public OWNER TO" when the owner is the bootstrap superuser, like how we skip
acl GRANT/REVOKE when the ACL matches the one recorded in pg_init_privs. I
waffled on that; would it be better to make the OWNER TO unconditional?
Like ownership, we've not been dumping security labels on the public schema.
The way I fixed ownership fixed security labels implicitly. If anyone thinks
I should unbundle these two, let me know.
All this is arguably a fix for an ancient bug. Some sites may need to
compensate for the behavior change, so I plan not to back-patch.
Thanks,
nm
Attachments:public-owner-dump-v1.patch (text/plain, 7.7K) download
Author: Noah Misch <noah@leadboat.com>
Commit: Noah Misch <noah@leadboat.com>
Dump public schema ownership and security labels.
Reviewed by FIXME.
Discussion: https://postgr.es/m/FIXME
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.cindex 1f82c64..a16d149 100644--- a/src/bin/pg_dump/pg_backup_archiver.c+++ b/src/bin/pg_dump/pg_backup_archiver.c@@ -3597,10 +3597,13 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
* Actually print the definition.
*
* Really crude hack for suppressing AUTHORIZATION clause that old pg_dump
- * versions put into CREATE SCHEMA. We have to do this when --no-owner- * mode is selected. This is ugly, but I see no other good way ...+ * versions put into CREATE SCHEMA. Don't mutate the modern definition+ * for schema "public", which consists of a comment. We have to do this+ * when --no-owner mode is selected. This is ugly, but I see no other+ * good way ...
*/
- if (ropt->noOwner && strcmp(te->desc, "SCHEMA") == 0)+ if (ropt->noOwner &&+ strcmp(te->desc, "SCHEMA") == 0 && strncmp(te->defn, "--", 2) != 0)
{
ahprintf(AH, "CREATE SCHEMA %s;\n\n\n", fmtId(te->tag));
}
@@ -3612,11 +3615,16 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData)
/*
* If we aren't using SET SESSION AUTH to determine ownership, we must
- * instead issue an ALTER OWNER command. We assume that anything without- * a DROP command is not a separately ownable object. All the categories- * with DROP commands must appear in one list or the other.+ * instead issue an ALTER OWNER command. Schema "public" is special; a+ * dump never creates it, so we use ALTER OWNER even when using SET+ * SESSION for all other objects. We assume that anything without a DROP+ * command is not a separately ownable object. All the categories with+ * DROP commands must appear in one list or the other.
*/
- if (!ropt->noOwner && !ropt->use_setsessauth &&+ if (!ropt->noOwner &&+ (!ropt->use_setsessauth ||+ (strcmp(te->tag, "public") == 0+ && strcmp(te->desc, "SCHEMA") == 0)) &&
te->owner && strlen(te->owner) > 0 &&
te->dropStmt && strlen(te->dropStmt) > 0)
{
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.cindex 1ab98a2..c633644 100644--- a/src/bin/pg_dump/pg_dump.c+++ b/src/bin/pg_dump/pg_dump.c@@ -44,6 +44,7 @@
#include "catalog/pg_aggregate_d.h"
#include "catalog/pg_am_d.h"
#include "catalog/pg_attribute_d.h"
+#include "catalog/pg_authid_d.h"
#include "catalog/pg_cast_d.h"
#include "catalog/pg_class_d.h"
#include "catalog/pg_collation_d.h"
@@ -1566,10 +1567,14 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
* no-mans-land between being a system object and a user object. We
* don't want to dump creation or comment commands for it, because
* that complicates matters for non-superuser use of pg_dump. But we
- * should dump any ACL changes that have occurred for it, and of- * course we should dump contained objects.+ * should dump any ownership changes, security labels, and ACL+ * changes, and of course we should dump contained objects. pg_dump+ * ties ownership to DUMP_COMPONENT_DEFINITION, so dump a "definition"+ * bearing just a comment.
*/
- nsinfo->dobj.dump = DUMP_COMPONENT_ACL;+ nsinfo->dobj.dump = DUMP_COMPONENT_ALL & ~DUMP_COMPONENT_COMMENT;+ if (nsinfo->nspowner == BOOTSTRAP_SUPERUSERID)+ nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
}
else
@@ -4748,6 +4753,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
int i_tableoid;
int i_oid;
int i_nspname;
+ int i_nspowner;
int i_rolname;
int i_nspacl;
int i_rnspacl;
@@ -4772,6 +4778,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
dopt->binary_upgrade);
appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
+ "n.nspowner, "
"(%s nspowner) AS rolname, "
"%s as nspacl, "
"%s as rnspacl, "
@@ -4796,7 +4803,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
destroyPQExpBuffer(init_racl_subquery);
}
else
- appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "+ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, nspowner, "
"(%s nspowner) AS rolname, "
"nspacl, NULL as rnspacl, "
"NULL AS initnspacl, NULL as initrnspacl "
@@ -4812,6 +4819,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
i_tableoid = PQfnumber(res, "tableoid");
i_oid = PQfnumber(res, "oid");
i_nspname = PQfnumber(res, "nspname");
+ i_nspowner = PQfnumber(res, "nspowner");
i_rolname = PQfnumber(res, "rolname");
i_nspacl = PQfnumber(res, "nspacl");
i_rnspacl = PQfnumber(res, "rnspacl");
@@ -4825,6 +4833,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&nsinfo[i].dobj);
nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
+ nsinfo[i].nspowner = atooid(PQgetvalue(res, i, i_nspowner));
nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl));
@@ -10315,9 +10324,19 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
- appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);-- appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);+ if (strcmp(nspinfo->dobj.name, "public") == 0)+ {+ /* see selectDumpableNamespace() */+ appendPQExpBufferStr(delq,+ "-- *not* dropping schema, since initdb creates it\n");+ appendPQExpBufferStr(q,+ "-- *not* creating schema, since initdb creates it\n");+ }+ else+ {+ appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);+ appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);+ }
if (dopt->binary_upgrade)
binary_upgrade_extension_member(q, &nspinfo->dobj,
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.hindex d7f77f1..d14787b 100644--- a/src/bin/pg_dump/pg_dump.h+++ b/src/bin/pg_dump/pg_dump.h@@ -141,6 +141,7 @@ typedef struct _dumpableObject
typedef struct _namespaceInfo
{
DumpableObject dobj;
+ Oid nspowner;
char *rolname; /* name of owner, or empty string */
char *nspacl;
char *rnspacl;
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.plindex 11dc98e..5a5a766 100644--- a/src/bin/pg_dump/t/002_pg_dump.pl+++ b/src/bin/pg_dump/t/002_pg_dump.pl@@ -605,6 +605,16 @@ my %tests = (
unlike => { no_owner => 1, },
},
+ 'ALTER SCHEMA public OWNER TO' => {+ create_order => 2,+ create_sql => 'ALTER SCHEMA public OWNER TO regress_dump_test_role;',+ regexp => qr/^ALTER SCHEMA public OWNER TO .+;/m,+ like => {+ %full_runs, section_pre_data => 1,+ },+ unlike => { no_owner => 1, },+ },+
'ALTER SEQUENCE test_table_col1_seq' => {
regexp => qr/^
\QALTER SEQUENCE dump_test.test_table_col1_seq OWNED BY dump_test.test_table.col1;\E
@@ -940,6 +950,13 @@ my %tests = (
like => {},
},
+ 'COMMENT ON SCHEMA public' => {+ regexp => qr/^COMMENT ON SCHEMA public IS .+;/m,++ # this shouldn't ever get emitted+ like => {},+ },+
'COMMENT ON TABLE dump_test.test_table' => {
create_order => 36,
create_sql => 'COMMENT ON TABLE dump_test.test_table
@@ -3229,6 +3246,7 @@ my %tests = (
create_sql => 'REVOKE CREATE ON SCHEMA public FROM public;',
regexp => qr/^
\QREVOKE ALL ON SCHEMA public FROM PUBLIC;\E
+ \n\QGRANT ALL ON SCHEMA public TO regress_dump_test_role;\E
\n\QGRANT USAGE ON SCHEMA public TO PUBLIC;\E
/xm,
like => { %full_runs, section_pre_data => 1, },
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: noah@leadboat.com
Subject: Re: Dump public schema ownership & seclabels
In-Reply-To: <20201229134924.GA1431748@rfd.leadboat.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox