All,
> It was brought up for discussion- see > > http://www.postgresql.org/message-id/20141015052259.gg28...@tamriel.snowman.net > > Specifically: > --------------- > One curious item to note is that the > current if(!superuser()) {} block approach has masked an inconsistency > in at least the FDW code which required a change to the regression > tests- namely, we normally force FDW owners to have USAGE rights on > the FDW, but that was being bypassed when a superuser makes the call. > I seriously doubt that was intentional. I won't push back if someone > really wants it to stay that way though. > --------------- > > No one mentioned any concerns with it (and three people replied), so I'm > inclined to think it's an agreeable change. Adam probably didn't > mention it with this patch simply because it had already been brought > up. Yes, this is correct, I was under the impression that this has already been addressed. Also, I thought it is a relatively benign change and perhaps even one for the better. With that said, I'll certainly leave it as is if that is the consensus. > > Which makes me wonder whether the other changes are indeed without > > effect or just not covered by tests. > > > > > * has_privilege-cleanup_11-5-2014.patch > > > > I don't really see the merit of this patch. A "cleanup" patch that adds > > more code than it removes isn't really a cleanup. If you want to make > > the APIs consistent, then let's make a patch for that. If you want to > > make the error messages consistent, then let's make a patch for that. > Fair enough. I think we all agree that fixing the superuser shortcuts are a relevant and welcome change (at least that is the sense I get). So, how about for the time being, we table the "consistent API and error messages" and focus solely on the shortcuts? If that is favorable, then I have attached a patch to address those changes. > There is other work going on replacing these role attributes with > > something more general, so maybe this cleanup should be done as part of > > that other work. > I agree and given the work that has already been done toward that effort I think that would perhaps be the better approach to addressing them. Perhaps 'cleanup' is just an overloaded term. The patch is to make the > APIs and the error messages consistent. I'm amazed at how much > discussion and work is going into these exceptionally minor changes > which have been already broken out of the larger and far more > interesting work (imv anyway). Having two patches, one to simply move > the checks around and then another to make the error messages in those > checks consistent, which will naturally end up depending on each other, > strikes me as overkill, but we can certainly do it. > Agreed, but will certainly do whatever is necessary to keep these changes moving forward. Though, I think the attached patch mitigates any need to break these changes out further. Andres raised a concern about the specific error message wording (which > was intended to just make it more consistent with the rest of our > permission-check error messages..), are there any other opinions on the > wording? Would be great to get feedback on that.. > Agreed, I would certainly be willing to move these changes forward as a separate effort, but without more specific recommendations beyond what has already been discussed and proposed I'm at a bit of a loss on where to take it. I'm all ears on this one and would certainly appreciate any feedback Thanks, Adam -- Adam Brightwell - adam.brightw...@crunchydatasolutions.com Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c new file mode 100644 index 133143d..510caf6 *** a/src/backend/access/transam/xlogfuncs.c --- b/src/backend/access/transam/xlogfuncs.c *************** pg_start_backup(PG_FUNCTION_ARGS) *** 54,60 **** backupidstr = text_to_cstring(backupid); ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); --- 54,60 ---- backupidstr = text_to_cstring(backupid); ! if (!has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); *************** pg_stop_backup(PG_FUNCTION_ARGS) *** 82,88 **** { XLogRecPtr stoppoint; ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); --- 82,88 ---- { XLogRecPtr stoppoint; ! if (!has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c new file mode 100644 index c9a9baf..3723066 *** a/src/backend/commands/alter.c --- b/src/backend/commands/alter.c *************** AlterObjectOwner_internal(Relation rel, *** 806,852 **** Datum *values; bool *nulls; bool *replaces; ! /* Superusers can bypass permission checks */ ! if (!superuser()) { ! AclObjectKind aclkind = get_object_aclkind(classId); ! /* must be owner */ ! if (!has_privs_of_role(GetUserId(), old_ownerId)) { ! char *objname; ! char namebuf[NAMEDATALEN]; ! ! if (Anum_name != InvalidAttrNumber) ! { ! datum = heap_getattr(oldtup, Anum_name, ! RelationGetDescr(rel), &isnull); ! Assert(!isnull); ! objname = NameStr(*DatumGetName(datum)); ! } ! else ! { ! snprintf(namebuf, sizeof(namebuf), "%u", ! HeapTupleGetOid(oldtup)); ! objname = namebuf; ! } ! aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname); } ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), new_ownerId); ! ! /* New owner must have CREATE privilege on namespace */ ! if (OidIsValid(namespaceId)) { ! AclResult aclresult; ! ! aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId, ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(namespaceId)); } } /* Build a modified tuple */ --- 806,847 ---- Datum *values; bool *nulls; bool *replaces; + AclObjectKind aclkind = get_object_aclkind(classId); ! /* must be owner */ ! if (!has_privs_of_role(GetUserId(), old_ownerId)) { ! char *objname; ! char namebuf[NAMEDATALEN]; ! if (Anum_name != InvalidAttrNumber) { ! datum = heap_getattr(oldtup, Anum_name, ! RelationGetDescr(rel), &isnull); ! Assert(!isnull); ! objname = NameStr(*DatumGetName(datum)); } ! else { ! snprintf(namebuf, sizeof(namebuf), "%u", ! HeapTupleGetOid(oldtup)); ! objname = namebuf; } + aclcheck_error(ACLCHECK_NOT_OWNER, aclkind, objname); + } + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), new_ownerId); + + /* New owner must have CREATE privilege on namespace */ + if (OidIsValid(namespaceId)) + { + AclResult aclresult; + + aclresult = pg_namespace_aclcheck(namespaceId, new_ownerId, + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); } /* Build a modified tuple */ diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c new file mode 100644 index ab4ed6c..aad6ae4 *** a/src/backend/commands/foreigncmds.c --- b/src/backend/commands/foreigncmds.c *************** AlterForeignServerOwner_internal(Relatio *** 332,361 **** if (form->srvowner != newOwnerId) { ! /* Superusers can always do it */ ! if (!superuser()) ! { ! Oid srvId; ! AclResult aclresult; ! srvId = HeapTupleGetOid(tup); ! /* Must be owner */ ! if (!pg_foreign_server_ownercheck(srvId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, ! NameStr(form->srvname)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have USAGE privilege on foreign-data wrapper */ ! aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ACL_USAGE); ! if (aclresult != ACLCHECK_OK) ! { ! ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw); ! aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); ! } } form->srvowner = newOwnerId; --- 332,359 ---- if (form->srvowner != newOwnerId) { ! Oid srvId; ! AclResult aclresult; ! srvId = HeapTupleGetOid(tup); ! /* Must be owner */ ! if (!pg_foreign_server_ownercheck(srvId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, ! NameStr(form->srvname)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have USAGE privilege on foreign-data wrapper */ ! aclresult = pg_foreign_data_wrapper_aclcheck(form->srvfdw, newOwnerId, ! ACL_USAGE); ! if (aclresult != ACLCHECK_OK) ! { ! ForeignDataWrapper *fdw = GetForeignDataWrapper(form->srvfdw); ! ! aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); } form->srvowner = newOwnerId; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c new file mode 100644 index 5629455..c0d4d4e *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** ATExecChangeOwner(Oid relationOid, Oid n *** 8618,8644 **** /* skip permission checks when recursing to index or toast table */ if (!recursing) { ! /* Superusers can always do it */ ! if (!superuser()) ! { ! Oid namespaceOid = tuple_class->relnamespace; ! AclResult aclresult; ! /* Otherwise, must be owner of the existing object */ ! if (!pg_class_ownercheck(relationOid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(target_rel)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have CREATE privilege on namespace */ ! aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(namespaceOid)); ! } } memset(repl_null, false, sizeof(repl_null)); --- 8618,8640 ---- /* skip permission checks when recursing to index or toast table */ if (!recursing) { ! Oid namespaceOid = tuple_class->relnamespace; ! AclResult aclresult; ! /* Must be owner of the existing object */ ! if (!pg_class_ownercheck(relationOid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(target_rel)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have CREATE privilege on namespace */ ! aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(namespaceOid)); } memset(repl_null, false, sizeof(repl_null)); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c new file mode 100644 index cbb65f8..b5904a8 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** AlterTypeOwner(List *names, Oid newOwner *** 3348,3371 **** */ if (typTup->typowner != newOwnerId) { ! /* Superusers can always do it */ ! if (!superuser()) ! { ! /* Otherwise, must be owner of the existing object */ ! if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId())) ! aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have CREATE privilege on namespace */ ! aclresult = pg_namespace_aclcheck(typTup->typnamespace, ! newOwnerId, ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(typTup->typnamespace)); ! } /* * If it's a composite type, invoke ATExecChangeOwner so that we fix --- 3348,3367 ---- */ if (typTup->typowner != newOwnerId) { ! /* Must be owner of the existing object */ ! if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId())) ! aclcheck_error_type(ACLCHECK_NOT_OWNER, HeapTupleGetOid(tup)); ! /* Must be able to become new owner */ ! check_is_member_of_role(GetUserId(), newOwnerId); ! /* New owner must have CREATE privilege on namespace */ ! aclresult = pg_namespace_aclcheck(typTup->typnamespace, ! newOwnerId, ! ACL_CREATE); ! if (aclresult != ACLCHECK_OK) ! aclcheck_error(aclresult, ACL_KIND_NAMESPACE, ! get_namespace_name(typTup->typnamespace)); /* * If it's a composite type, invoke ATExecChangeOwner so that we fix diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c new file mode 100644 index 1977f09..1941c42 *** a/src/backend/replication/logical/logicalfuncs.c --- b/src/backend/replication/logical/logicalfuncs.c *************** XLogRead(char *buf, TimeLineID tli, XLog *** 205,211 **** static void check_permissions(void) { ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); --- 205,211 ---- static void check_permissions(void) { ! if (!has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c new file mode 100644 index bd4701f..7211161 *** a/src/backend/replication/slotfuncs.c --- b/src/backend/replication/slotfuncs.c *************** *** 26,32 **** static void check_permissions(void) { ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); --- 26,32 ---- static void check_permissions(void) { ! if (!has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c new file mode 100644 index 8fccb4c..679396d *** a/src/backend/utils/init/miscinit.c --- b/src/backend/utils/init/miscinit.c *************** has_rolreplication(Oid roleid) *** 337,342 **** --- 337,346 ---- bool result = false; HeapTuple utup; + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (HeapTupleIsValid(utup)) { diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c new file mode 100644 index c348034..ec0ac6a *** a/src/backend/utils/init/postinit.c --- b/src/backend/utils/init/postinit.c *************** InitPostgres(const char *in_dbname, Oid *** 762,768 **** { Assert(!bootstrap); ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); --- 762,768 ---- { Assert(!bootstrap); ! if (!has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out new file mode 100644 index e4dedb0..b3b5cd0 *** a/src/test/regress/expected/foreign_data.out --- b/src/test/regress/expected/foreign_data.out *************** ERROR: must be owner of foreign server *** 394,399 **** --- 394,400 ---- ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR ERROR: must be owner of foreign server s1 RESET ROLE; + GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; ALTER SERVER s1 OWNER TO regress_test_role; GRANT regress_test_role2 TO regress_test_role; SET ROLE regress_test_role; *************** GRANT USAGE ON FOREIGN DATA WRAPPER foo *** 417,422 **** --- 418,424 ---- SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; + REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; DROP ROLE regress_test_indirect; -- ERROR ERROR: role "regress_test_indirect" cannot be dropped because some objects depend on it DETAIL: owner of server s1 diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql new file mode 100644 index de9dbc8..91d51c9 *** a/src/test/regress/sql/foreign_data.sql --- b/src/test/regress/sql/foreign_data.sql *************** SET ROLE regress_test_role; *** 164,169 **** --- 164,170 ---- ALTER SERVER s1 VERSION '1.1'; -- ERROR ALTER SERVER s1 OWNER TO regress_test_role; -- ERROR RESET ROLE; + GRANT USAGE ON FOREIGN DATA WRAPPER foo TO regress_test_role; ALTER SERVER s1 OWNER TO regress_test_role; GRANT regress_test_role2 TO regress_test_role; SET ROLE regress_test_role; *************** GRANT USAGE ON FOREIGN DATA WRAPPER foo *** 183,188 **** --- 184,190 ---- SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; RESET ROLE; + REVOKE USAGE ON FOREIGN DATA WRAPPER foo FROM regress_test_role; DROP ROLE regress_test_indirect; -- ERROR \des+
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers