From daf3bc2c18c456e9ca7c8a122b3c5d34f550243a Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 26 Aug 2022 15:17:41 -0400 Subject: [PATCH v1] Add a SET option to the GRANT command. Similar to how the INHERIT option controls whether or not the permissions of the granted role are automatically available to the grantee, the new SET permission controls whether or not the grantee may use the SET ROLE command to assume the privileges of the granted role. --- doc/src/sgml/catalogs.sgml | 11 ++++ doc/src/sgml/func.sgml | 9 ++-- doc/src/sgml/ref/grant.sgml | 32 +++++++---- doc/src/sgml/ref/revoke.sgml | 8 +-- doc/src/sgml/ref/set_role.sgml | 9 ++-- doc/src/sgml/user-manag.sgml | 20 +++++-- src/backend/commands/user.c | 44 +++++++++++++-- src/backend/commands/variable.c | 2 +- src/backend/utils/adt/acl.c | 68 +++++++++++++++++++----- src/bin/pg_dump/pg_dumpall.c | 23 +++++--- src/include/catalog/pg_auth_members.h | 1 + src/include/utils/acl.h | 1 + src/test/regress/expected/privileges.out | 9 ++++ src/test/regress/sql/privileges.sql | 9 ++++ 14 files changed, 199 insertions(+), 47 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 00f833d210..9ed2b020b7 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1727,6 +1727,17 @@ SCRAM-SHA-256$<iteration count>:&l granted role + + + + set_option bool + + + True if the member can + SET ROLE + to the granted role + + diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index f87afefeae..e7e5eca7e5 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -24070,11 +24070,14 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); Does user have privilege for role? Allowable privilege types are - MEMBER and USAGE. + MEMBER, USAGE, + and SET. MEMBER denotes direct or indirect membership in - the role (that is, the right to do SET ROLE), while + the role without regard to what specific privileges may be conferred. USAGE denotes whether the privileges of the role - are immediately available without doing SET ROLE. + are immediately available without doing SET ROLE, + while SET denotes whether it is possible to change + to the role using the SET ROLE command. This function does not allow the special case of setting user to public, because the PUBLIC pseudo-role can never be a member of real roles. diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml index dea19cd348..d5911ff942 100644 --- a/doc/src/sgml/ref/grant.sgml +++ b/doc/src/sgml/ref/grant.sgml @@ -98,7 +98,7 @@ GRANT { USAGE | ALL [ PRIVILEGES ] } [ GRANTED BY role_specification ] GRANT role_name [, ...] TO role_specification [, ...] - [ WITH { ADMIN | INHERIT } { OPTION | TRUE | FALSE } ] + [ WITH { ADMIN | INHERIT | SET } { OPTION | TRUE | FALSE } ] [ GRANTED BY role_specification ] where role_specification can be: @@ -250,17 +250,17 @@ GRANT role_name [, ...] TO This variant of the GRANT command grants membership in a role to one or more other roles. Membership in a role is significant - because it conveys the privileges granted to a role to each of its - members. + because it potentially allows access to the privileges granted to a role + to each of its members, and potentially also the ability to make changes + to the role itself. However, the actualy permisions conferred depend on + the options associated with the grant. - The effect of membership in a role can be modified by specifying the - ADMIN or INHERIT option, each - of which can be set to either TRUE or - FALSE. The keyword OPTION is accepted - as a synonym for TRUE, so that - WITH ADMIN OPTION + Each of the options described below can be set to either + TRUE or FALSE. The keyword + OPTION is accepted as a synonym for + TRUE, so that WITH ADMIN OPTION is a synonym for WITH ADMIN TRUE. @@ -272,7 +272,8 @@ GRANT role_name [, ...] TO on itself. Database superusers can grant or revoke membership in any role to anyone. Roles having CREATEROLE privilege can grant or revoke membership - in any role that is not a superuser. + in any role that is not a superuser. This option defaults to + FALSE. @@ -287,6 +288,17 @@ GRANT role_name [, ...] TO CREATE ROLE. + + The SET option, if it is set to + TRUE, allows the member to change to the granted + role using the + SET ROLE + command. If a role is an indirect member of another role, it can use + SET ROLE to change to that role only if there is a + chain of grants each of which has SET TRUE. + This option defaults to TRUE. + + If GRANTED BY is specified, the grant is recorded as having been done by the specified role. A user can only attribute a grant diff --git a/doc/src/sgml/ref/revoke.sgml b/doc/src/sgml/ref/revoke.sgml index 4fd4bfb3d7..2db66bbf37 100644 --- a/doc/src/sgml/ref/revoke.sgml +++ b/doc/src/sgml/ref/revoke.sgml @@ -125,7 +125,7 @@ REVOKE [ GRANT OPTION FOR ] [ GRANTED BY role_specification ] [ CASCADE | RESTRICT ] -REVOKE [ { ADMIN | INHERIT } OPTION FOR ] +REVOKE [ { ADMIN | INHERIT | SET } OPTION FOR ] role_name [, ...] FROM role_specification [, ...] [ GRANTED BY role_specification ] [ CASCADE | RESTRICT ] @@ -209,9 +209,9 @@ REVOKE [ { ADMIN | INHERIT } OPTION FOR ] Just as ADMIN OPTION can be removed from an existing - role grant, it is also possible to revoke INHERIT OPTION. - This is equivalent to setting the value of that option to - FALSE. + role grant, it is also possible to revoke INHERIT OPTION + or SET OPTION. This is equivalent to setting the value + of the corresponding option to FALSE. diff --git a/doc/src/sgml/ref/set_role.sgml b/doc/src/sgml/ref/set_role.sgml index deecfe4120..13bad1bf66 100644 --- a/doc/src/sgml/ref/set_role.sgml +++ b/doc/src/sgml/ref/set_role.sgml @@ -77,14 +77,17 @@ RESET ROLE effectively drops all the privileges except for those which the target role directly possesses or inherits. On the other hand, if the session user role has been granted memberships WITH INHERIT FALSE, the - privileges of the granted roles can't be accessed by default. However, the + privileges of the granted roles can't be accessed by default. However, if + the role was granted WITH SET TRUE, the session user can use SET ROLE to drop the privileges assigned directly to the session user and instead acquire the privileges - available to the named role. + available to the named role. If the role was granted WITH INHERIT + FALSE, SET FALSE then the privileges of that role cannot be + exercised either with or without SET ROLE. - In particular, when a superuser chooses to SET ROLE to a + Note that when a superuser chooses to SET ROLE to a non-superuser role, they lose their superuser privileges. diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml index fc836d5748..601fff3e6b 100644 --- a/doc/src/sgml/user-manag.sgml +++ b/doc/src/sgml/user-manag.sgml @@ -354,7 +354,8 @@ REVOKE group_role FROM role1 The members of a group role can use the privileges of the role in two - ways. First, every member of a group can explicitly do + ways. First, member roles that have been granted membership with the + SET option can do SET ROLE to temporarily become the group role. In this state, the database session has access to the privileges of the group role rather @@ -369,13 +370,16 @@ REVOKE group_role FROM role1 Immediately after connecting as role joe, a database session will have use of privileges granted directly to joe - plus any privileges granted to admin, because joe - inherits admin's privileges. However, privileges + plus any privileges granted to admin and + island, because joe + inherits those privileges. However, privileges granted to wheel are not available, because even though joe is indirectly a member of wheel, the membership is via admin which was granted using @@ -384,7 +388,8 @@ GRANT wheel TO admin WITH INHERIT FALSE; SET ROLE admin; the session would have use of only those privileges granted to - admin, and not those granted to joe. After: + admin, and not those granted to joe or + island. After: SET ROLE wheel; @@ -402,9 +407,14 @@ RESET ROLE; The SET ROLE command always allows selecting any role - that the original login role is directly or indirectly a member of. + that the original login role is directly or indirectly a member of, + provided that there is a chain of membership grants each of which has + SET TRUE (which is the default). Thus, in the above example, it is not necessary to become admin before becoming wheel. + On the other hand, it is not possible to become island + at all; joe can only access those privileges via + inheritance. diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 265a48af7e..61456d6723 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 e5ddcda0b4..f907b59746 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -881,7 +881,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 fd71a9b13e..355ba98cc8 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -61,16 +61,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; @@ -4685,10 +4686,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} }; @@ -4717,6 +4721,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; } @@ -4765,15 +4774,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 @@ -4864,6 +4875,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 @@ -4903,9 +4918,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) @@ -4927,6 +4943,34 @@ has_privs_of_role(Oid member, Oid role) role); } +/* + * Can member use SET ROLE to this role? + * + * 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. + * + * It doesn't matter whether the grants are inheritable. That's a separate + * question; see has_privs_of_role. + */ +bool +member_can_set_role(Oid member, Oid role) +{ + /* Fast path for simple case */ + if (member == role) + return true; + + /* Superusers have every privilege, so can always SET ROLE */ + if (superuser_arg(member)) + return true; + + /* + * 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_SETROLE, + InvalidOid, NULL), + role); +} /* * Is member a member of role (directly or indirectly)? diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index d665b257c9..75c09596c4 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -953,8 +953,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 @@ -966,10 +967,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, " @@ -977,8 +978,8 @@ dumpRoleMembership(PGconn *conn) "ug.oid AS grantorid, " "ug.rolname AS grantor, " "a.admin_option"); - if (dump_inherit_option) - appendPQExpBuffer(buf, ", a.inherit_option"); + if (dump_grant_options) + appendPQExpBuffer(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 " @@ -987,6 +988,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"); @@ -1057,6 +1059,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. */ @@ -1067,6 +1070,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 @@ -1096,7 +1101,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; @@ -1107,6 +1112,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 3ee6ae5f6a..b145fce1ed 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 3d6411197c..48de5d5c01 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -209,6 +209,7 @@ 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 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); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index bd3453ee91..309a15abdc 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; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 4ad366470d..7a169f5860 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -114,6 +114,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 +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; -- 2.24.3 (Apple Git-128)