diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/alter.c | 2 | ||||
-rw-r--r-- | src/backend/commands/dbcommands.c | 4 | ||||
-rw-r--r-- | src/backend/commands/foreigncmds.c | 2 | ||||
-rw-r--r-- | src/backend/commands/publicationcmds.c | 2 | ||||
-rw-r--r-- | src/backend/commands/schemacmds.c | 4 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 2 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 2 | ||||
-rw-r--r-- | src/backend/commands/user.c | 44 | ||||
-rw-r--r-- | src/backend/commands/variable.c | 2 | ||||
-rw-r--r-- | src/backend/utils/adt/acl.c | 106 | ||||
-rw-r--r-- | src/bin/pg_dump/pg_dumpall.c | 23 | ||||
-rw-r--r-- | src/include/catalog/pg_auth_members.h | 1 | ||||
-rw-r--r-- | src/include/utils/acl.h | 3 | ||||
-rw-r--r-- | src/test/regress/expected/alter_generic.out | 38 | ||||
-rw-r--r-- | src/test/regress/expected/foreign_data.out | 2 | ||||
-rw-r--r-- | src/test/regress/expected/privileges.out | 41 | ||||
-rw-r--r-- | src/test/regress/sql/privileges.sql | 39 |
17 files changed, 251 insertions, 66 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index b2089d785b6..10b6fe19a2c 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -999,7 +999,7 @@ AlterObjectOwner_internal(Relation rel, Oid objectId, Oid new_ownerId) objname); } /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), new_ownerId); + check_can_set_role(GetUserId(), new_ownerId); /* New owner must have CREATE privilege on namespace */ if (OidIsValid(namespaceId)) diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index a67ea86619c..6eb87427181 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -941,7 +941,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create database"))); - check_is_member_of_role(GetUserId(), datdba); + check_can_set_role(GetUserId(), datdba); /* * Lookup database (template) to be cloned, and obtain share lock on it. @@ -2495,7 +2495,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) dbname); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* * must have createdb rights diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 55b0be9e1d1..28b5c75f110 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -363,7 +363,7 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) NameStr(form->srvname)); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* New owner must have USAGE privilege on foreign-data wrapper */ aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE); diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c index 940655b9be0..20fa72c5c80 100644 --- a/src/backend/commands/publicationcmds.c +++ b/src/backend/commands/publicationcmds.c @@ -1911,7 +1911,7 @@ AlterPublicationOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) NameStr(form->pubname)); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on database */ aclresult = object_aclcheck(DatabaseRelationId, MyDatabaseId, newOwnerId, ACL_CREATE); diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index b03f07a2322..12cbfba7d06 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -97,7 +97,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString, aclcheck_error(aclresult, OBJECT_DATABASE, get_database_name(MyDatabaseId)); - check_is_member_of_role(saved_uid, owner_uid); + check_can_set_role(saved_uid, owner_uid); /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) @@ -370,7 +370,7 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) NameStr(nspForm->nspname)); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* * must have create-schema rights diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f0068078520..845208d662b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13833,7 +13833,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock RelationGetRelationName(target_rel)); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId, diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index ecc8b3f44c9..7770a86bee0 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -3745,7 +3745,7 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype) aclcheck_error_type(ACLCHECK_NOT_OWNER, typTup->oid); /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); + check_can_set_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = object_aclcheck(NamespaceRelationId, typTup->typnamespace, diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 2369cc600c7..8b6543edee2 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -51,8 +51,8 @@ * RRG_REMOVE_ADMIN_OPTION indicates a grant that would need to have * admin_option set to false by the operation. * - * RRG_REMOVE_INHERIT_OPTION indicates a grant that would need to have - * inherit_option set to false by the operation. + * Similarly, RRG_REMOVE_INHERIT_OPTION and RRG_REMOVE_SET_OPTION indicate + * grants that would need to have the corresponding options set to false. * * RRG_DELETE_GRANT indicates a grant that would need to be removed entirely * by the operation. @@ -62,6 +62,7 @@ typedef enum RRG_NOOP, RRG_REMOVE_ADMIN_OPTION, RRG_REMOVE_INHERIT_OPTION, + RRG_REMOVE_SET_OPTION, RRG_DELETE_GRANT } RevokeRoleGrantAction; @@ -73,10 +74,12 @@ typedef struct unsigned specified; bool admin; bool inherit; + bool set; } GrantRoleOptions; #define GRANT_ROLE_SPECIFIED_ADMIN 0x0001 #define GRANT_ROLE_SPECIFIED_INHERIT 0x0002 +#define GRANT_ROLE_SPECIFIED_SET 0x0004 /* GUC parameter */ int Password_encryption = PASSWORD_TYPE_SCRAM_SHA_256; @@ -1389,6 +1392,12 @@ GrantRole(ParseState *pstate, GrantRoleStmt *stmt) if (parse_bool(optval, &popt.inherit)) continue; } + else if (strcmp(opt->defname, "set") == 0) + { + popt.specified |= GRANT_ROLE_SPECIFIED_SET; + if (parse_bool(optval, &popt.set)) + continue; + } else ereport(ERROR, errcode(ERRCODE_SYNTAX_ERROR), @@ -1776,6 +1785,16 @@ AddRoleMems(const char *rolename, Oid roleid, at_least_one_change = true; } + if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0 + && authmem_form->set_option != popt->set) + { + new_record[Anum_pg_auth_members_set_option - 1] = + BoolGetDatum(popt->set); + new_record_repl[Anum_pg_auth_members_set_option - 1] = + true; + at_least_one_change = true; + } + if (!at_least_one_change) { ereport(NOTICE, @@ -1798,9 +1817,15 @@ AddRoleMems(const char *rolename, Oid roleid, Oid objectId; Oid *newmembers = palloc(sizeof(Oid)); - /* Set admin option if user set it to true, otherwise not. */ + /* + * The values for these options can be taken directly from 'popt'. + * Either they were specified, or the defaults as set by + * InitGrantRoleOptions are correct. + */ new_record[Anum_pg_auth_members_admin_option - 1] = BoolGetDatum(popt->admin); + new_record[Anum_pg_auth_members_set_option - 1] = + BoolGetDatum(popt->set); /* * If the user specified a value for the inherit option, use @@ -1989,6 +2014,13 @@ DelRoleMems(const char *rolename, Oid roleid, new_record_repl[Anum_pg_auth_members_inherit_option - 1] = true; } + else if (actions[i] == RRG_REMOVE_SET_OPTION) + { + new_record[Anum_pg_auth_members_set_option - 1] = + BoolGetDatum(false); + new_record_repl[Anum_pg_auth_members_set_option - 1] = + true; + } else elog(ERROR, "unknown role revoke action"); @@ -2182,6 +2214,11 @@ plan_single_revoke(CatCList *memlist, RevokeRoleGrantAction *actions, */ actions[i] = RRG_REMOVE_INHERIT_OPTION; } + else if ((popt->specified & GRANT_ROLE_SPECIFIED_SET) != 0) + { + /* Here too, no need to recurse. */ + actions[i] = RRG_REMOVE_SET_OPTION; + } else { bool revoke_admin_option_only; @@ -2331,4 +2368,5 @@ InitGrantRoleOptions(GrantRoleOptions *popt) popt->specified = 0; popt->admin = false; popt->inherit = false; + popt->set = true; } diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 791bac6715c..00d8d54d820 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -939,7 +939,7 @@ check_role(char **newval, void **extra, GucSource source) * leader's state. */ if (!InitializingParallelWorker && - !is_member_of_role(GetSessionUserId(), roleid)) + !member_can_set_role(GetSessionUserId(), roleid)) { if (source == PGC_S_TEST) { diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 8bdb9461b7f..d4d68f97243 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -67,16 +67,17 @@ typedef struct * * Each element of cached_roles is an OID list of constituent roles for the * corresponding element of cached_role (always including the cached_role - * itself). One cache has ROLERECURSE_PRIVS semantics, and the other has - * ROLERECURSE_MEMBERS semantics. + * itself). There's a separate cache for each RoleRecurseType, with the + * corresponding semantics. */ enum RoleRecurseType { - ROLERECURSE_PRIVS = 0, /* recurse through inheritable grants */ - ROLERECURSE_MEMBERS = 1 /* recurse unconditionally */ + ROLERECURSE_MEMBERS = 0, /* recurse unconditionally */ + ROLERECURSE_PRIVS = 1, /* recurse through inheritable grants */ + ROLERECURSE_SETROLE = 2 /* recurse through grants with set_option */ }; -static Oid cached_role[] = {InvalidOid, InvalidOid}; -static List *cached_roles[] = {NIL, NIL}; +static Oid cached_role[] = {InvalidOid, InvalidOid, InvalidOid}; +static List *cached_roles[] = {NIL, NIL, NIL}; static uint32 cached_db_hash; @@ -4691,10 +4692,13 @@ convert_role_priv_string(text *priv_type_text) static const priv_map role_priv_map[] = { {"USAGE", ACL_USAGE}, {"MEMBER", ACL_CREATE}, + {"SET", ACL_SET}, {"USAGE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, {"USAGE WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, {"MEMBER WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, {"MEMBER WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, + {"SET WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, + {"SET WITH ADMIN OPTION", ACL_GRANT_OPTION_FOR(ACL_CREATE)}, {NULL, 0} }; @@ -4723,6 +4727,11 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) if (has_privs_of_role(roleid, role_oid)) return ACLCHECK_OK; } + if (mode & ACL_SET) + { + if (member_can_set_role(roleid, role_oid)) + return ACLCHECK_OK; + } return ACLCHECK_NO_PRIV; } @@ -4771,15 +4780,17 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue) } /* Force membership caches to be recomputed on next use */ - cached_role[ROLERECURSE_PRIVS] = InvalidOid; cached_role[ROLERECURSE_MEMBERS] = InvalidOid; + cached_role[ROLERECURSE_PRIVS] = InvalidOid; + cached_role[ROLERECURSE_SETROLE] = InvalidOid; } /* * Get a list of roles that the specified roleid is a member of * - * Type ROLERECURSE_PRIVS recurses only through inheritable grants, - * while ROLERECURSE_MEMBERS recurses through all grants. + * Type ROLERECURSE_MEMBERS recurses through all grants; ROLERECURSE_PRIVS + * recurses only through inheritable grants; and ROLERECURSE_SETROLe recurses + * only through grants with set_option. * * Since indirect membership testing is relatively expensive, we cache * a list of memberships. Hence, the result is only guaranteed good until @@ -4870,6 +4881,10 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type, if (type == ROLERECURSE_PRIVS && !form->inherit_option) continue; + /* If we're supposed to ignore non-SET grants, do so. */ + if (type == ROLERECURSE_SETROLE && !form->set_option) + continue; + /* * Even though there shouldn't be any loops in the membership * graph, we must test for having already seen this role. It is @@ -4909,9 +4924,10 @@ roles_is_member_of(Oid roleid, enum RoleRecurseType type, /* * Does member have the privileges of role (directly or indirectly)? * - * This is defined not to recurse through grants that are not inherited; - * in such cases, membership implies the ability to do SET ROLE, but - * the privileges are not available until you've done so. + * This is defined not to recurse through grants that are not inherited, + * and only inherited grants confer the associated privileges automatically. + * + * See also member_can_set_role, below. */ bool has_privs_of_role(Oid member, Oid role) @@ -4933,49 +4949,87 @@ has_privs_of_role(Oid member, Oid role) role); } - /* - * Is member a member of role (directly or indirectly)? + * Can member use SET ROLE to this role? * - * This is defined to recurse through grants whether they are inherited or not. + * There must be a chain of grants from 'member' to 'role' each of which + * permits SET ROLE; that is, each of which has set_option = true. * - * Do not use this for privilege checking, instead use has_privs_of_role() + * It doesn't matter whether the grants are inheritable. That's a separate + * question; see has_privs_of_role. + * + * This function should be used to determine whether the session user can + * use SET ROLE to become the target user. We also use it to determine whether + * the session user can change an existing object to be owned by the target + * user, or create new objects owned by the target user. */ bool -is_member_of_role(Oid member, Oid role) +member_can_set_role(Oid member, Oid role) { /* Fast path for simple case */ if (member == role) return true; - /* Superusers have every privilege, so are part of every role */ + /* Superusers have every privilege, so can always SET ROLE */ if (superuser_arg(member)) return true; /* - * Find all the roles that member is a member of, including multi-level - * recursion, then see if target role is any one of them. + * Find all the roles that member can access via SET ROLE, including + * multi-level recursion, then see if target role is any one of them. */ - return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS, + return list_member_oid(roles_is_member_of(member, ROLERECURSE_SETROLE, InvalidOid, NULL), role); } /* - * check_is_member_of_role - * is_member_of_role with a standard permission-violation error if not + * Permission violation eror unless able to SET ROLE to target role. */ void -check_is_member_of_role(Oid member, Oid role) +check_can_set_role(Oid member, Oid role) { - if (!is_member_of_role(member, role)) + if (!member_can_set_role(member, role)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be member of role \"%s\"", + errmsg("must be able to SET ROLE \"%s\"", GetUserNameFromId(role, false)))); } /* + * Is member a member of role (directly or indirectly)? + * + * This is defined to recurse through grants whether they are inherited or not. + * + * Do not use this for privilege checking, instead use has_privs_of_role(). + * Don't use it for determining whether it's possible to SET ROLE to some + * other role; for that, use member_can_set_role(). And don't use it for + * determining whether it's OK to create an object owned by some other role: + * use member_can_set_role() for that, too. + * + * In short, calling this function is the wrong thing to do nearly everywhere. + */ +bool +is_member_of_role(Oid member, Oid role) +{ + /* Fast path for simple case */ + if (member == role) + return true; + + /* Superusers have every privilege, so are part of every role */ + if (superuser_arg(member)) + return true; + + /* + * Find all the roles that member is a member of, including multi-level + * recursion, then see if target role is any one of them. + */ + return list_member_oid(roles_is_member_of(member, ROLERECURSE_MEMBERS, + InvalidOid, NULL), + role); +} + +/* * Is member a member of role, not considering superuserness? * * This is identical to is_member_of_role except we ignore superuser diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 083012ca39d..76a186b6394 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -955,8 +955,9 @@ dumpRoleMembership(PGconn *conn) end, total; bool dump_grantors; - bool dump_inherit_option; + bool dump_grant_options; int i_inherit_option; + int i_set_option; /* * Previous versions of PostgreSQL didn't used to track the grantor very @@ -968,10 +969,10 @@ dumpRoleMembership(PGconn *conn) dump_grantors = (PQserverVersion(conn) >= 160000); /* - * Previous versions of PostgreSQL also did not have a grant-level + * Previous versions of PostgreSQL also did not have grant-level options. * INHERIT option. */ - dump_inherit_option = (server_version >= 160000); + dump_grant_options = (server_version >= 160000); /* Generate and execute query. */ printfPQExpBuffer(buf, "SELECT ur.rolname AS role, " @@ -979,8 +980,8 @@ dumpRoleMembership(PGconn *conn) "ug.oid AS grantorid, " "ug.rolname AS grantor, " "a.admin_option"); - if (dump_inherit_option) - appendPQExpBufferStr(buf, ", a.inherit_option"); + if (dump_grant_options) + appendPQExpBufferStr(buf, ", a.inherit_option, a.set_option"); appendPQExpBuffer(buf, " FROM pg_auth_members a " "LEFT JOIN %s ur on ur.oid = a.roleid " "LEFT JOIN %s um on um.oid = a.member " @@ -989,6 +990,7 @@ dumpRoleMembership(PGconn *conn) "ORDER BY 1,2,4", role_catalog, role_catalog, role_catalog); res = executeQuery(conn, buf->data); i_inherit_option = PQfnumber(res, "inherit_option"); + i_set_option = PQfnumber(res, "set_option"); if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Role memberships\n--\n\n"); @@ -1059,6 +1061,7 @@ dumpRoleMembership(PGconn *conn) char *admin_option; char *grantorid; char *grantor; + char *set_option = "true"; bool found; /* If we already did this grant, don't do it again. */ @@ -1069,6 +1072,8 @@ dumpRoleMembership(PGconn *conn) grantorid = PQgetvalue(res, i, 2); grantor = PQgetvalue(res, i, 3); admin_option = PQgetvalue(res, i, 4); + if (dump_grant_options) + set_option = PQgetvalue(res, i, i_set_option); /* * If we're not dumping grantors or if the grantor is the @@ -1098,7 +1103,7 @@ dumpRoleMembership(PGconn *conn) fprintf(OPF, " TO %s", fmtId(member)); if (*admin_option == 't') appendPQExpBufferStr(optbuf, "ADMIN OPTION"); - if (dump_inherit_option) + if (dump_grant_options) { char *inherit_option; @@ -1109,6 +1114,12 @@ dumpRoleMembership(PGconn *conn) *inherit_option == 't' ? "TRUE" : "FALSE"); } + if (*set_option != 't') + { + if (optbuf->data[0] != '\0') + appendPQExpBufferStr(optbuf, ", "); + appendPQExpBuffer(optbuf, "SET FALSE"); + } if (optbuf->data[0] != '\0') fprintf(OPF, " WITH %s", optbuf->data); if (dump_grantors) diff --git a/src/include/catalog/pg_auth_members.h b/src/include/catalog/pg_auth_members.h index 3ee6ae5f6a4..b145fce1ed8 100644 --- a/src/include/catalog/pg_auth_members.h +++ b/src/include/catalog/pg_auth_members.h @@ -35,6 +35,7 @@ CATALOG(pg_auth_members,1261,AuthMemRelationId) BKI_SHARED_RELATION BKI_ROWTYPE_ Oid grantor BKI_LOOKUP(pg_authid); /* who granted the membership */ bool admin_option; /* granted with admin option? */ bool inherit_option; /* exercise privileges without SET ROLE? */ + bool set_option; /* use SET ROLE to the target role? */ } FormData_pg_auth_members; /* ---------------- diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 35b3d8dd88b..afbfdccf53c 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -209,11 +209,12 @@ extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId, extern int aclmembers(const Acl *acl, Oid **roleids); extern bool has_privs_of_role(Oid member, Oid role); +extern bool member_can_set_role(Oid member, Oid role); +extern void check_can_set_role(Oid member, Oid role); extern bool is_member_of_role(Oid member, Oid role); extern bool is_member_of_role_nosuper(Oid member, Oid role); extern bool is_admin_of_role(Oid member, Oid role); extern Oid select_best_admin(Oid member, Oid role); -extern void check_is_member_of_role(Oid member, Oid role); extern Oid get_role_oid(const char *rolname, bool missing_ok); extern Oid get_role_oid_or_public(const char *rolname); extern Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok); diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out index 54d3fe5764d..ae54cb254f9 100644 --- a/src/test/regress/expected/alter_generic.out +++ b/src/test/regress/expected/alter_generic.out @@ -46,7 +46,7 @@ ALTER FUNCTION alt_func1(int) RENAME TO alt_func2; -- failed (name conflict) ERROR: function alt_func2(integer) already exists in schema "alt_nsp1" ALTER FUNCTION alt_func1(int) RENAME TO alt_func3; -- OK ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- OK ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp1; -- OK, already there ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- OK @@ -54,7 +54,7 @@ ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg2; -- failed (name conflict) ERROR: function alt_agg2(integer) already exists in schema "alt_nsp1" ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg3; -- OK ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- OK ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -74,7 +74,7 @@ ALTER FUNCTION alt_func1(int) RENAME TO alt_func4; -- OK ALTER FUNCTION alt_func3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of function alt_func3 ALTER FUNCTION alt_func2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER FUNCTION alt_func3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of function alt_func3 ALTER FUNCTION alt_func2(int) SET SCHEMA alt_nsp2; -- failed (name conflicts) @@ -85,7 +85,7 @@ ALTER AGGREGATE alt_agg1(int) RENAME TO alt_agg4; -- OK ALTER AGGREGATE alt_agg3(int) OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of function alt_agg3 ALTER AGGREGATE alt_agg2(int) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER AGGREGATE alt_agg3(int) SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of function alt_agg3 ALTER AGGREGATE alt_agg2(int) SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -122,7 +122,7 @@ ALTER CONVERSION alt_conv1 RENAME TO alt_conv2; -- failed (name conflict) ERROR: conversion "alt_conv2" already exists in schema "alt_nsp1" ALTER CONVERSION alt_conv1 RENAME TO alt_conv3; -- OK ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- OK ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -134,7 +134,7 @@ ALTER CONVERSION alt_conv1 RENAME TO alt_conv4; -- OK ALTER CONVERSION alt_conv3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of conversion alt_conv3 ALTER CONVERSION alt_conv2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER CONVERSION alt_conv3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of conversion alt_conv3 ALTER CONVERSION alt_conv2 SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -196,7 +196,7 @@ ALTER LANGUAGE alt_lang1 RENAME TO alt_lang3; -- OK ALTER LANGUAGE alt_lang2 OWNER TO regress_alter_generic_user3; -- failed (not owner) ERROR: must be owner of language alt_lang2 ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER LANGUAGE alt_lang3 OWNER TO regress_alter_generic_user3; -- OK RESET SESSION AUTHORIZATION; SELECT lanname, a.rolname @@ -216,7 +216,7 @@ SET SESSION AUTHORIZATION regress_alter_generic_user1; CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); CREATE OPERATOR @+@ ( leftarg = int4, rightarg = int4, procedure = int4pl ); ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR @-@(int4, int4) SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -224,7 +224,7 @@ CREATE OPERATOR @-@ ( leftarg = int4, rightarg = int4, procedure = int4mi ); ALTER OPERATOR @+@(int4, int4) OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of operator @+@ ALTER OPERATOR @-@(int4, int4) OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER OPERATOR @+@(int4, int4) SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of operator @+@ -- can't test this: the error message includes the raw oid of namespace @@ -259,14 +259,14 @@ ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf2; -- failed (name c ERROR: operator family "alt_opf2" for access method "hash" already exists in schema "alt_nsp1" ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf3; -- OK ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- OK ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc2; -- failed (name conflict) ERROR: operator class "alt_opc2" for access method "hash" already exists in schema "alt_nsp1" ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc3; -- OK ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- OK ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- OK RESET SESSION AUTHORIZATION; @@ -285,7 +285,7 @@ ALTER OPERATOR FAMILY alt_opf1 USING hash RENAME TO alt_opf4; -- OK ALTER OPERATOR FAMILY alt_opf3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of operator family alt_opf3 ALTER OPERATOR FAMILY alt_opf2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER OPERATOR FAMILY alt_opf3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of operator family alt_opf3 ALTER OPERATOR FAMILY alt_opf2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -296,7 +296,7 @@ ALTER OPERATOR CLASS alt_opc1 USING hash RENAME TO alt_opc4; -- OK ALTER OPERATOR CLASS alt_opc3 USING hash OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of operator class alt_opc3 ALTER OPERATOR CLASS alt_opc2 USING hash OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER OPERATOR CLASS alt_opc3 USING hash SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of operator class alt_opc3 ALTER OPERATOR CLASS alt_opc2 USING hash SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -531,7 +531,7 @@ ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ERROR: statistics object "alt_stat2" already exists in schema "alt_nsp1" ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- OK ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -544,7 +544,7 @@ ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK ALTER STATISTICS alt_stat3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of statistics object alt_stat3 ALTER STATISTICS alt_stat2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER STATISTICS alt_stat3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of statistics object alt_stat3 ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -573,7 +573,7 @@ ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict2; -- failed (na ERROR: text search dictionary "alt_ts_dict2" already exists in schema "alt_nsp1" ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -585,7 +585,7 @@ ALTER TEXT SEARCH DICTIONARY alt_ts_dict1 RENAME TO alt_ts_dict4; -- OK ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of text search dictionary alt_ts_dict3 ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER TEXT SEARCH DICTIONARY alt_ts_dict3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of text search dictionary alt_ts_dict3 ALTER TEXT SEARCH DICTIONARY alt_ts_dict2 SET SCHEMA alt_nsp2; -- failed (name conflict) @@ -614,7 +614,7 @@ ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf2; -- failed ERROR: text search configuration "alt_ts_conf2" already exists in schema "alt_nsp1" ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user2; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user2" +ERROR: must be able to SET ROLE "regress_alter_generic_user2" ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_generic_user2; @@ -626,7 +626,7 @@ ALTER TEXT SEARCH CONFIGURATION alt_ts_conf1 RENAME TO alt_ts_conf4; -- OK ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 OWNER TO regress_alter_generic_user2; -- failed (not owner) ERROR: must be owner of text search configuration alt_ts_conf3 ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 OWNER TO regress_alter_generic_user3; -- failed (no role membership) -ERROR: must be member of role "regress_alter_generic_user3" +ERROR: must be able to SET ROLE "regress_alter_generic_user3" ALTER TEXT SEARCH CONFIGURATION alt_ts_conf3 SET SCHEMA alt_nsp2; -- failed (not owner) ERROR: must be owner of text search configuration alt_ts_conf3 ALTER TEXT SEARCH CONFIGURATION alt_ts_conf2 SET SCHEMA alt_nsp2; -- failed (name conflict) diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index 47bf56adbf2..5b30ee49f3e 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -442,7 +442,7 @@ ERROR: invalid option "foo" ALTER SERVER s8 OPTIONS (connect_timeout '30', SET dbname 'db1', DROP host); SET ROLE regress_test_role; ALTER SERVER s1 OWNER TO regress_test_indirect; -- ERROR -ERROR: must be member of role "regress_test_indirect" +ERROR: must be able to SET ROLE "regress_test_indirect" RESET ROLE; GRANT regress_test_indirect TO regress_test_role; SET ROLE regress_test_role; diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index bd3453ee914..a497db94a82 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -132,6 +132,15 @@ SET SESSION AUTHORIZATION regress_priv_user8; SET ROLE pg_read_all_settings; RESET ROLE; RESET SESSION AUTHORIZATION; +REVOKE SET OPTION FOR pg_read_all_settings FROM regress_priv_user8; +GRANT pg_read_all_stats TO regress_priv_user8 WITH SET FALSE; +SET SESSION AUTHORIZATION regress_priv_user8; +SET ROLE pg_read_all_settings; -- fail, no SET option any more +ERROR: permission denied to set role "pg_read_all_settings" +SET ROLE pg_read_all_stats; -- fail, granted without SET option +ERROR: permission denied to set role "pg_read_all_stats" +RESET ROLE; +RESET SESSION AUTHORIZATION; REVOKE pg_read_all_settings FROM regress_priv_user8; DROP USER regress_priv_user10; DROP USER regress_priv_user9; @@ -2809,3 +2818,35 @@ DROP ROLE regress_group; DROP ROLE regress_group_direct_manager; DROP ROLE regress_group_indirect_manager; DROP ROLE regress_group_member; +-- test SET and INHERIT options with object ownership changes +CREATE ROLE regress_roleoption_protagonist; +CREATE ROLE regress_roleoption_donor; +CREATE ROLE regress_roleoption_recipient; +CREATE SCHEMA regress_roleoption; +GRANT CREATE, USAGE ON SCHEMA regress_roleoption TO PUBLIC; +GRANT regress_roleoption_donor TO regress_roleoption_protagonist WITH INHERIT TRUE, SET FALSE; +GRANT regress_roleoption_recipient TO regress_roleoption_protagonist WITH INHERIT FALSE, SET TRUE; +SET SESSION AUTHORIZATION regress_roleoption_protagonist; +CREATE TABLE regress_roleoption.t1 (a int); +CREATE TABLE regress_roleoption.t2 (a int); +SET SESSION AUTHORIZATION regress_roleoption_donor; +CREATE TABLE regress_roleoption.t3 (a int); +SET SESSION AUTHORIZATION regress_roleoption_recipient; +CREATE TABLE regress_roleoption.t4 (a int); +SET SESSION AUTHORIZATION regress_roleoption_protagonist; +ALTER TABLE regress_roleoption.t1 OWNER TO regress_roleoption_donor; -- fails, can't be come donor +ERROR: must be able to SET ROLE "regress_roleoption_donor" +ALTER TABLE regress_roleoption.t2 OWNER TO regress_roleoption_recipient; -- works +ALTER TABLE regress_roleoption.t3 OWNER TO regress_roleoption_protagonist; -- works +ALTER TABLE regress_roleoption.t4 OWNER TO regress_roleoption_protagonist; -- fails, we don't inherit from recipient +ERROR: must be owner of table t4 +RESET SESSION AUTHORIZATION; +DROP TABLE regress_roleoption.t1; +DROP TABLE regress_roleoption.t2; +DROP TABLE regress_roleoption.t3; +DROP TABLE regress_roleoption.t4; +DROP SCHEMA regress_roleoption; +DROP ROLE regress_roleoption_recipient; +DROP ROLE regress_roleoption_donor; +DROP ROLE regress_roleoption_donor; +ERROR: role "regress_roleoption_donor" does not exist diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 4ad366470d9..daecf0ec64c 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -115,6 +115,15 @@ SET ROLE pg_read_all_settings; RESET ROLE; RESET SESSION AUTHORIZATION; +REVOKE SET OPTION FOR pg_read_all_settings FROM regress_priv_user8; +GRANT pg_read_all_stats TO regress_priv_user8 WITH SET FALSE; + +SET SESSION AUTHORIZATION regress_priv_user8; +SET ROLE pg_read_all_settings; -- fail, no SET option any more +SET ROLE pg_read_all_stats; -- fail, granted without SET option +RESET ROLE; + +RESET SESSION AUTHORIZATION; REVOKE pg_read_all_settings FROM regress_priv_user8; DROP USER regress_priv_user10; @@ -1813,3 +1822,33 @@ DROP ROLE regress_group; DROP ROLE regress_group_direct_manager; DROP ROLE regress_group_indirect_manager; DROP ROLE regress_group_member; + +-- test SET and INHERIT options with object ownership changes +CREATE ROLE regress_roleoption_protagonist; +CREATE ROLE regress_roleoption_donor; +CREATE ROLE regress_roleoption_recipient; +CREATE SCHEMA regress_roleoption; +GRANT CREATE, USAGE ON SCHEMA regress_roleoption TO PUBLIC; +GRANT regress_roleoption_donor TO regress_roleoption_protagonist WITH INHERIT TRUE, SET FALSE; +GRANT regress_roleoption_recipient TO regress_roleoption_protagonist WITH INHERIT FALSE, SET TRUE; +SET SESSION AUTHORIZATION regress_roleoption_protagonist; +CREATE TABLE regress_roleoption.t1 (a int); +CREATE TABLE regress_roleoption.t2 (a int); +SET SESSION AUTHORIZATION regress_roleoption_donor; +CREATE TABLE regress_roleoption.t3 (a int); +SET SESSION AUTHORIZATION regress_roleoption_recipient; +CREATE TABLE regress_roleoption.t4 (a int); +SET SESSION AUTHORIZATION regress_roleoption_protagonist; +ALTER TABLE regress_roleoption.t1 OWNER TO regress_roleoption_donor; -- fails, can't be come donor +ALTER TABLE regress_roleoption.t2 OWNER TO regress_roleoption_recipient; -- works +ALTER TABLE regress_roleoption.t3 OWNER TO regress_roleoption_protagonist; -- works +ALTER TABLE regress_roleoption.t4 OWNER TO regress_roleoption_protagonist; -- fails, we don't inherit from recipient +RESET SESSION AUTHORIZATION; +DROP TABLE regress_roleoption.t1; +DROP TABLE regress_roleoption.t2; +DROP TABLE regress_roleoption.t3; +DROP TABLE regress_roleoption.t4; +DROP SCHEMA regress_roleoption; +DROP ROLE regress_roleoption_recipient; +DROP ROLE regress_roleoption_donor; +DROP ROLE regress_roleoption_donor; |