* Alvaro Herrera (alvhe...@2ndquadrant.com) wrote: > > As I started looking at this, there are multiple other places where > > these types of error messages occur (opclasscmds.c, user.c, > > postinit.c, miscinit.c are just a few), not just around the changes in > > this patch. If we change them in one place, wouldn't it be best to > > change them in the rest? If that is the case, I'm afraid that might > > distract from the purpose of this patch. Perhaps, if we want to > > change them, then that should be submitted as a separate patch? > > Yeah. I'm just saying that maybe this patch should adopt whatever > wording we agree to, not that we need to change other places. On the > other hand, since so many other places have adopted the different > wording, maybe there's a reason for it and if so, does anybody know what > it is. But I have to say that it does look inconsistent to me.
Updated patch attached. Comments welcome. Thanks! Stephen
diff --git a/contrib/test_decoding/expected/permissions.out b/contrib/test_decoding/expected/permissions.out new file mode 100644 index 212fd1d..f011955 *** a/contrib/test_decoding/expected/permissions.out --- b/contrib/test_decoding/expected/permissions.out *************** RESET ROLE; *** 54,66 **** -- plain user *can't* can control replication SET ROLE lr_normal; SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); ! ERROR: must be superuser or replication role to use replication slots INSERT INTO lr_test VALUES('lr_superuser_init'); ERROR: permission denied for relation lr_test SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); ! ERROR: must be superuser or replication role to use replication slots SELECT pg_drop_replication_slot('regression_slot'); ! ERROR: must be superuser or replication role to use replication slots RESET ROLE; -- replication users can drop superuser created slots SET ROLE lr_superuser; --- 54,69 ---- -- plain user *can't* can control replication SET ROLE lr_normal; SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); ! ERROR: permission denied to use replication slots ! HINT: You must be superuser or replication role to use replication slots. INSERT INTO lr_test VALUES('lr_superuser_init'); ERROR: permission denied for relation lr_test SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); ! ERROR: permission denied to use replication slots ! HINT: You must be superuser or replication role to use replication slots. SELECT pg_drop_replication_slot('regression_slot'); ! ERROR: permission denied to use replication slots ! HINT: You must be superuser or replication role to use replication slots. RESET ROLE; -- replication users can drop superuser created slots SET ROLE lr_superuser; *************** SELECT 'init' FROM pg_create_logical_rep *** 90,96 **** RESET ROLE; SET ROLE lr_normal; SELECT pg_drop_replication_slot('regression_slot'); ! ERROR: must be superuser or replication role to use replication slots RESET ROLE; -- all users can see existing slots SET ROLE lr_superuser; --- 93,100 ---- RESET ROLE; SET ROLE lr_normal; SELECT pg_drop_replication_slot('regression_slot'); ! ERROR: permission denied to use replication slots ! HINT: You must be superuser or replication role to use replication slots. RESET ROLE; -- all users can see existing slots SET ROLE lr_superuser; diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c new file mode 100644 index 133143d..a05de7e *** a/src/backend/access/transam/xlogfuncs.c --- b/src/backend/access/transam/xlogfuncs.c *************** *** 27,32 **** --- 27,33 ---- #include "miscadmin.h" #include "replication/walreceiver.h" #include "storage/smgr.h" + #include "utils/acl.h" #include "utils/builtins.h" #include "utils/numeric.h" #include "utils/guc.h" *************** pg_start_backup(PG_FUNCTION_ARGS) *** 54,63 **** 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"))); startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL); --- 55,65 ---- backupidstr = text_to_cstring(backupid); ! if (!has_replication_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to run a backup"), ! errhint("You must be superuser or replication role to run a backup."))); startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL); *************** pg_stop_backup(PG_FUNCTION_ARGS) *** 82,91 **** { XLogRecPtr stoppoint; ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! (errmsg("must be superuser or replication role to run a backup")))); stoppoint = do_pg_stop_backup(NULL, true, NULL); --- 84,94 ---- { XLogRecPtr stoppoint; ! if (!has_replication_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to run a backup"), ! errhint("You must be superuser or replication role to run a backup."))); stoppoint = do_pg_stop_backup(NULL, true, NULL); diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c new file mode 100644 index d30612c..4d26b02 *** a/src/backend/catalog/aclchk.c --- b/src/backend/catalog/aclchk.c *************** static AclMode restrict_and_check_grant( *** 143,148 **** --- 143,149 ---- AttrNumber att_number, const char *colname); static AclMode pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mask, AclMaskHow how); + static bool has_catupdate_privilege(Oid roleid); #ifdef ACLDEBUG *************** aclcheck_error_type(AclResult aclerr, Oi *** 3425,3431 **** /* Check if given user has rolcatupdate privilege according to pg_authid */ static bool ! has_rolcatupdate(Oid roleid) { bool rolcatupdate; HeapTuple tuple; --- 3426,3432 ---- /* Check if given user has rolcatupdate privilege according to pg_authid */ static bool ! has_catupdate_privilege(Oid roleid) { bool rolcatupdate; HeapTuple tuple; *************** pg_class_aclmask(Oid table_oid, Oid role *** 3630,3636 **** if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) && IsSystemClass(table_oid, classForm) && classForm->relkind != RELKIND_VIEW && ! !has_rolcatupdate(roleid) && !allowSystemTableMods) { #ifdef ACLDEBUG --- 3631,3637 ---- if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) && IsSystemClass(table_oid, classForm) && classForm->relkind != RELKIND_VIEW && ! !has_catupdate_privilege(roleid) && !allowSystemTableMods) { #ifdef ACLDEBUG *************** has_createrole_privilege(Oid roleid) *** 5078,5083 **** --- 5079,5106 ---- ReleaseSysCache(utup); } return result; + } + + /* + * Check whether specified role has REPLICATION priviledge (or is a superuser) + */ + bool + has_replication_privilege(Oid roleid) + { + bool result = false; + HeapTuple utup; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); + if (HeapTupleIsValid(utup)) + { + result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication; + ReleaseSysCache(utup); + } + return result; } bool 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 ecdff1e..5fb470f *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** ATExecChangeOwner(Oid relationOid, Oid n *** 8625,8651 **** /* 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)); --- 8625,8647 ---- /* 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 55a6881..c25e237 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** AlterTypeOwner(List *names, Oid newOwner *** 3302,3325 **** */ 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 --- 3302,3321 ---- */ 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/commands/user.c b/src/backend/commands/user.c new file mode 100644 index 1a73fd8..fd9a4ba *** a/src/backend/commands/user.c --- b/src/backend/commands/user.c *************** static void DelRoleMems(const char *role *** 55,69 **** List *memberNames, List *memberIds, bool admin_opt); - - /* Check if current user has createrole privileges */ - static bool - have_createrole_privilege(void) - { - return has_createrole_privilege(GetUserId()); - } - - /* * CREATE ROLE */ --- 55,60 ---- *************** CreateRole(CreateRoleStmt *stmt) *** 304,310 **** } else { ! if (!have_createrole_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create role"))); --- 295,301 ---- } else { ! if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create role"))); *************** AlterRole(AlterRoleStmt *stmt) *** 682,688 **** (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change bypassrls attribute"))); } ! else if (!have_createrole_privilege()) { if (!(inherit < 0 && createrole < 0 && --- 673,679 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change bypassrls attribute"))); } ! else if (!has_createrole_privilege(GetUserId())) { if (!(inherit < 0 && createrole < 0 && *************** AlterRoleSet(AlterRoleSetStmt *stmt) *** 898,904 **** } else { ! if (!have_createrole_privilege() && HeapTupleGetOid(roletuple) != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), --- 889,895 ---- } else { ! if (!has_createrole_privilege(GetUserId()) && HeapTupleGetOid(roletuple) != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** DropRole(DropRoleStmt *stmt) *** 951,957 **** pg_auth_members_rel; ListCell *item; ! if (!have_createrole_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to drop role"))); --- 942,948 ---- pg_auth_members_rel; ListCell *item; ! if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to drop role"))); *************** RenameRole(const char *oldname, const ch *** 1182,1188 **** } else { ! if (!have_createrole_privilege()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename role"))); --- 1173,1179 ---- } else { ! if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename role"))); *************** AddRoleMems(const char *rolename, Oid ro *** 1409,1415 **** } else { ! if (!have_createrole_privilege() && !is_admin_of_role(grantorId, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), --- 1400,1406 ---- } else { ! if (!has_createrole_privilege(GetUserId()) && !is_admin_of_role(grantorId, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** DelRoleMems(const char *rolename, Oid ro *** 1555,1561 **** } else { ! if (!have_createrole_privilege() && !is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), --- 1546,1552 ---- } else { ! if (!has_createrole_privilege(GetUserId()) && !is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c new file mode 100644 index 3a5ec2f..1b40f7e *** a/src/backend/replication/logical/logicalfuncs.c --- b/src/backend/replication/logical/logicalfuncs.c *************** *** 27,32 **** --- 27,33 ---- #include "mb/pg_wchar.h" + #include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/inval.h" *************** XLogRead(char *buf, TimeLineID tli, XLog *** 200,214 **** } } - 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")))); - } - /* * read_page callback for logical decoding contexts. * --- 201,206 ---- *************** pg_logical_slot_get_changes_guts(Functio *** 322,328 **** if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! check_permissions(); CheckLogicalDecodingRequirements(); --- 314,324 ---- if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! if (!has_replication_privilege(GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to use replication slots"), ! errhint("You must be superuser or replication role to use replication slots."))); CheckLogicalDecodingRequirements(); diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c new file mode 100644 index bd4701f..4cb82d0 *** a/src/backend/replication/slotfuncs.c --- b/src/backend/replication/slotfuncs.c *************** *** 20,37 **** #include "replication/slot.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" #include "utils/builtins.h" #include "utils/pg_lsn.h" - 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")))); - } - /* * SQL function for creating a new physical (streaming replication) * replication slot. --- 20,29 ---- #include "replication/slot.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" + #include "utils/acl.h" #include "utils/builtins.h" #include "utils/pg_lsn.h" /* * SQL function for creating a new physical (streaming replication) * replication slot. *************** pg_create_physical_replication_slot(PG_F *** 51,57 **** if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! check_permissions(); CheckSlotRequirements(); --- 43,53 ---- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! if (!has_replication_privilege(GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to use replication slots"), ! errhint("You must be superuser or replication role to use replication slots."))); CheckSlotRequirements(); *************** pg_create_logical_replication_slot(PG_FU *** 94,100 **** if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! check_permissions(); CheckLogicalDecodingRequirements(); --- 90,100 ---- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); ! if (!has_replication_privilege(GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to use replication slots"), ! errhint("You must be superuser or replication role to use replication slots."))); CheckLogicalDecodingRequirements(); *************** pg_drop_replication_slot(PG_FUNCTION_ARG *** 143,149 **** { Name name = PG_GETARG_NAME(0); ! check_permissions(); CheckSlotRequirements(); --- 143,153 ---- { Name name = PG_GETARG_NAME(0); ! if (!has_replication_privilege(GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to use replication slots"), ! errhint("You must be superuser or replication role to use replication slots."))); CheckSlotRequirements(); diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c new file mode 100644 index dc6eb2c..4f9ffe5 *** a/src/backend/utils/adt/acl.c --- b/src/backend/utils/adt/acl.c *************** static AclMode convert_role_priv_string( *** 117,122 **** --- 117,123 ---- static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode); static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue); + static bool has_inherit_privilege(Oid roleid); /* *************** RoleMembershipCacheCallback(Datum arg, i *** 4636,4642 **** /* Check if specified role has rolinherit set */ static bool ! has_rolinherit(Oid roleid) { bool result = false; HeapTuple utup; --- 4637,4643 ---- /* Check if specified role has rolinherit set */ static bool ! has_inherit_privilege(Oid roleid) { bool result = false; HeapTuple utup; *************** roles_has_privs_of(Oid roleid) *** 4697,4703 **** int i; /* Ignore non-inheriting roles */ ! if (!has_rolinherit(memberid)) continue; /* Find roles that memberid is directly a member of */ --- 4698,4704 ---- int i; /* Ignore non-inheriting roles */ ! if (!has_inherit_privilege(memberid)) continue; /* Find roles that memberid is directly a member of */ diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c new file mode 100644 index 8fccb4c..73d5cd2 *** a/src/backend/utils/init/miscinit.c --- b/src/backend/utils/init/miscinit.c *************** SetUserIdAndContext(Oid userid, bool sec *** 327,351 **** SecurityRestrictionContext &= ~SECURITY_LOCAL_USERID_CHANGE; } - - /* - * Check whether specified role has explicit REPLICATION privilege - */ - bool - has_rolreplication(Oid roleid) - { - bool result = false; - HeapTuple utup; - - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication; - ReleaseSysCache(utup); - } - return result; - } - /* * Initialize user identity during normal backend startup */ --- 327,332 ---- diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c new file mode 100644 index 6a6a445..1133d1f *** a/src/backend/utils/init/postinit.c --- b/src/backend/utils/init/postinit.c *************** InitPostgres(const char *in_dbname, Oid *** 761,770 **** { Assert(!bootstrap); ! if (!superuser() && !has_rolreplication(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser or replication role to start walsender"))); } /* --- 761,771 ---- { Assert(!bootstrap); ! if (!has_replication_privilege(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("permission denied to use replication slots"), ! errhint("You must be superuser or replication role to use replication slots."))); } /* diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h new file mode 100644 index 1558a75..0cbc917 *** a/src/include/miscadmin.h --- b/src/include/miscadmin.h *************** extern void ValidatePgVersion(const char *** 435,441 **** extern void process_shared_preload_libraries(void); extern void process_session_preload_libraries(void); extern void pg_bindtextdomain(const char *domain); - extern bool has_rolreplication(Oid roleid); /* in access/transam/xlog.c */ extern bool BackupInProgress(void); --- 435,440 ---- diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h new file mode 100644 index a8e3164..1c52fdc *** a/src/include/utils/acl.h --- b/src/include/utils/acl.h *************** extern bool pg_event_trigger_ownercheck( *** 328,332 **** --- 328,333 ---- extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); extern bool has_bypassrls_privilege(Oid roleid); + extern bool has_replication_privilege(Oid roleid); #endif /* ACL_H */ 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+
signature.asc
Description: Digital signature