Adam, * Adam Brightwell (adam.brightw...@crunchydatasolutions.com) wrote: > I have attached and updated patch for review.
Thanks! I've gone over this and made quite a few documentation and comment updates, but not too much else, so I'm pretty happy with how this is coming along. As mentioned elsewhere, this conflicts with the GetUserId() to has_privs_of_role() cleanup, but as I anticipate handling both this patch and that one, I'll find some way to manage. :) Updated patch attached. Barring objections, I'll be moving forward with this soonish. Would certainly appreciate any additional testing or review that you (or anyone!) has time to provide. Thanks again! Stephen
From 58efbb5ebeadca15fb2f3ada4ca5c83b7ddd5d69 Mon Sep 17 00:00:00 2001 From: Stephen Frost <sfr...@snowman.net> Date: Sat, 28 Feb 2015 20:23:38 -0500 Subject: [PATCH] Add role attributes for various operations Historically, many operations have been restricted to the superuser. These additional role attributes allow an administrator to delegate the right to run some of these operations out to other roles, reducing the number of cases which strictly require superuser rights and therefore, hopefully, reducing the number of users running as superuser in the wild. This patch introduces the following role attributes: EXCLUSIVEBACKUP - allows the role to run pg_start_backup, pg_stop_backup, pg_create_restore_point, and pg_switch_xlog. XLOGREPLAY - allows the role to run pg_xlog_replay_pause and pg_xlog_replay_resume. LOGFILE - allows the role to run pg_rotate_logfile MONITOR - allows the role to see the details of all running processes (including both normal backends and replication backends). The documentation and code are also updated to use has_privs_of_role(). SIGNAL - allows the role to signal all other normal backends (except those initiated by a superuser) with pg_cancel_backend and pg_termiante_backend. The documentation and code for the NOSIGNAL case are also updated to use has_privs_of_role(). In passing, this also centralizes and standardizes the REPLICATION and CREATEROLE support functions. Lots of discussion, review, and commentary from Jim Nasby, Simon, Magnus, Alvaro, and Robert. Authored by Adam, bugs and various documentation updates from me. --- doc/src/sgml/catalogs.sgml | 30 ++++ doc/src/sgml/func.sgml | 38 +++-- doc/src/sgml/ref/create_role.sgml | 81 +++++++++++ doc/src/sgml/ref/create_user.sgml | 6 + src/backend/access/transam/xlogfuncs.c | 27 ++-- src/backend/catalog/aclchk.c | 137 ++++++++++++++++++ src/backend/commands/user.c | 183 ++++++++++++++++++++++--- src/backend/parser/gram.y | 20 +++ src/backend/replication/logical/logicalfuncs.c | 15 +- src/backend/replication/slotfuncs.c | 25 ++-- src/backend/replication/walsender.c | 8 +- src/backend/utils/adt/misc.c | 19 ++- src/backend/utils/adt/pgstatfuncs.c | 54 ++++++-- src/backend/utils/init/miscinit.c | 19 --- src/backend/utils/init/postinit.c | 2 +- src/include/catalog/pg_authid.h | 20 ++- src/include/miscadmin.h | 1 - src/include/utils/acl.h | 6 + 18 files changed, 585 insertions(+), 106 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 515a40e..77b1090 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1454,6 +1454,36 @@ </row> <row> + <entry><structfield>rolexclbackup</structfield></entry> + <entry><type>bool</type></entry> + <entry>Role can perform on-line exclusive backup operations</entry> + </row> + + <row> + <entry><structfield>rolxlogreplay</structfield></entry> + <entry><type>bool</type></entry> + <entry>Role can control xlog recovery replay operations</entry> + </row> + + <row> + <entry><structfield>rollogfile</structfield></entry> + <entry><type>bool</type></entry> + <entry>Role can rotate log files</entry> + </row> + + <row> + <entry><structfield>rolmonitor</structfield></entry> + <entry><type>bool</type></entry> + <entry>Role can view pg_stat_* details</entry> + </row> + + <row> + <entry><structfield>rolsignal</structfield></entry> + <entry><type>bool</type></entry> + <entry>Role can signal normal backend processes</entry> + </row> + + <row> <entry><structfield>rolconnlimit</structfield></entry> <entry><type>int4</type></entry> <entry> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index da2ed67..7842cfc 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -16261,8 +16261,7 @@ SELECT set_config('log_statement_stats', 'off', false); <para> The functions shown in <xref linkend="functions-admin-signal-table"> send control signals to - other server processes. Use of these functions is usually restricted - to superusers, with noted exceptions. + other server processes. </para> <table id="functions-admin-signal-table"> @@ -16279,9 +16278,11 @@ SELECT set_config('log_statement_stats', 'off', false); <literal><function>pg_cancel_backend(<parameter>pid</parameter> <type>int</>)</function></literal> </entry> <entry><type>boolean</type></entry> - <entry>Cancel a backend's current query. You can execute this against - another backend that has exactly the same role as the user calling the - function. In all other cases, you must be a superuser. + <entry>Cancel a backend's current query. A user can execute this against + another backend which the user owns or which is owned by a role that the user + is a member of. Roles with the SIGNAL attribute can signal all other normal + backends except those initiated by superusers. Superusers can signal all + backends. </entry> </row> <row> @@ -16289,24 +16290,29 @@ SELECT set_config('log_statement_stats', 'off', false); <literal><function>pg_reload_conf()</function></literal> </entry> <entry><type>boolean</type></entry> - <entry>Cause server processes to reload their configuration files</entry> + <entry>Cause server processes to reload their configuration files. Only a + superuser can send this signal. + </entry> </row> <row> <entry> <literal><function>pg_rotate_logfile()</function></literal> </entry> <entry><type>boolean</type></entry> - <entry>Rotate server's log file</entry> + <entry>Rotate server's log file. Roles with the LOGFILE attribute + or who are superusers can send this signal. + </entry> </row> <row> <entry> <literal><function>pg_terminate_backend(<parameter>pid</parameter> <type>int</>)</function></literal> </entry> <entry><type>boolean</type></entry> - <entry>Terminate a backend. You can execute this against - another backend that has exactly the same role as the user - calling the function. In all other cases, you must be a - superuser. + <entry>Terminate a backend. A user can execute this against + another backend which the user owns or which is owned by a role that + the user is a member of. Roles with the SIGNAL attribute can signal + all other normal backends except those initiated by superuser. + Superusers can signal all backends. </entry> </row> </tbody> @@ -16431,14 +16437,14 @@ SELECT set_config('log_statement_stats', 'off', false); <literal><function>pg_start_backup(<parameter>label</> <type>text</> <optional>, <parameter>fast</> <type>boolean</> </optional>)</function></literal> </entry> <entry><type>pg_lsn</type></entry> - <entry>Prepare for performing on-line backup (restricted to superusers or replication roles)</entry> + <entry>Prepare for performing on-line exclusive backup (restricted to superusers, replication roles, and exclusive backup roles)</entry> </row> <row> <entry> <literal><function>pg_stop_backup()</function></literal> </entry> <entry><type>pg_lsn</type></entry> - <entry>Finish performing on-line backup (restricted to superusers or replication roles)</entry> + <entry>Finish performing on-line exclusive backup (restricted to superusers, replication roles, and exclusive backup roles)</entry> </row> <row> <entry> @@ -16713,7 +16719,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); <literal><function>pg_xlog_replay_pause()</function></literal> </entry> <entry><type>void</type></entry> - <entry>Pauses recovery immediately (restricted to superusers). + <entry>Pauses recovery immediately (only users with the XLOGREPLAY + attribute and superusers can run this function). </entry> </row> <row> @@ -16721,7 +16728,8 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); <literal><function>pg_xlog_replay_resume()</function></literal> </entry> <entry><type>void</type></entry> - <entry>Restarts recovery if it was paused (restricted to superusers). + <entry>Restarts recovery if it was paused (only users with the + XLOGREPLAY attribute and superusers can run this function). </entry> </row> </tbody> diff --git a/doc/src/sgml/ref/create_role.sgml b/doc/src/sgml/ref/create_role.sgml index ea26027..cee2c37 100644 --- a/doc/src/sgml/ref/create_role.sgml +++ b/doc/src/sgml/ref/create_role.sgml @@ -33,6 +33,11 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION | BYPASSRLS | NOBYPASSRLS + | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP + | XLOGREPLAY | NOXLOGREPLAY + | LOGFILE | NOLOGFILE + | MONITOR | NOMONITOR + | SIGNAL | NOSIGNAL | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable> | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>' @@ -211,6 +216,82 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac </varlistentry> <varlistentry> + <term><literal>EXCLUSIVEBACKUP</literal></term> + <term><literal>NOEXCLUSIVEBACKUP</literal></term> + <listitem> + <para> + These clauses determine whether a role is allowed to start/stop an + on-line exclusive backup. An on-line exclusive backup is one that is + initiated by the low-level base backup interface instead of through the + replication protocol, see <xref linkend="backup-lowlevel-base-backup">. + If not specified, <literal>NOEXCLUSIVEBACKUP</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>XLOGREPLAY</literal></term> + <term><literal>NOXLOGREPLAY</literal></term> + <listitem> + <para> + These clauses determine whether a role is allowed to pause/resume xlog + recovery using the pg_xlog_replay_pause and pg_xlog_replay_resume + functions. See <xref linkend="functions-recovery-control-table"> for + details. + If not specified, <literal>NOXLOGREPLAY</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>LOGFILE</literal></term> + <term><literal>NOLOGFILE</literal></term> + <listitem> + <para> + These clauses determine whether a role is allowed to rotate log files + using the pg_rotate_logfile function. See <xref + linkend="functions-admin-signal"> for details. + If not specified, <literal>NOLOGFILE</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>MONITOR</literal></term> + <term><literal>NOMONITOR</literal></term> + <listitem> + <para> + These clauses determine whether a role is allowed to view the details + of all processes through the pg_stat_* functions, pg_stat_activity + and pg_stat_replication views. Roles with MONITOR will see the + detailed information about all processes, normal and replication, while + roles with NOMONITOR will only see the detailed information for processes + they own, or which are owned by roles which they are a member of. + See <xref linkend="pg-stat-activity-view"> for details. + If not specified, <literal>NOMONITOR</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>SIGNAL</literal></term> + <term><literal>NOSIGNAL</literal></term> + <listitem> + <para> + These clauses determine whether a role is allowed to signal other normal + backend processes. A role having the <literal>SIGNAL</literal> attribute + will be allowed to signal any backend processes except those initiated by + a superuser. Roles with NOSIGNAL are only allowed to signal processes + they own, or which are owned by roles which they are a member of. + Signals are able to be sent to normal backends processes using + pg_terminate_backend and pg_cancel_backend, see <xref linkend="functions-admin-signal"> + for further information. + If not specified, <literal>NOSIGNAL</literal> is the default. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>CONNECTION LIMIT</literal> <replaceable class="parameter">connlimit</replaceable></term> <listitem> <para> diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml index 065999c..f7f10c7 100644 --- a/doc/src/sgml/ref/create_user.sgml +++ b/doc/src/sgml/ref/create_user.sgml @@ -32,6 +32,12 @@ CREATE USER <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac | INHERIT | NOINHERIT | LOGIN | NOLOGIN | REPLICATION | NOREPLICATION + | BYPASSRLS | NOBYPASSRLS + | EXCLUSIVEBACKUP | NOEXCLUSIVEBACKUP + | XLOGREPLAY | NOXLOGREPLAY + | LOGFILE | NOLOGFILE + | MONITOR | NOMONITOR + | SIGNAL | NOSIGNAL | CONNECTION LIMIT <replaceable class="PARAMETER">connlimit</replaceable> | [ ENCRYPTED | UNENCRYPTED ] PASSWORD '<replaceable class="PARAMETER">password</replaceable>' | VALID UNTIL '<replaceable class="PARAMETER">timestamp</replaceable>' diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 2179bf7..12b8a17 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -27,6 +27,7 @@ #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" @@ -54,10 +55,11 @@ pg_start_backup(PG_FUNCTION_ARGS) backupidstr = text_to_cstring(backupid); - if (!superuser() && !has_rolreplication(GetUserId())) + if (!has_replication_privilege(GetUserId()) + && !has_exclbackup_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser or replication role to run a backup"))); + errmsg("must be superuser, replication role or exclusive backup role to run a backup"))); startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL); @@ -82,10 +84,11 @@ pg_stop_backup(PG_FUNCTION_ARGS) { XLogRecPtr stoppoint; - if (!superuser() && !has_rolreplication(GetUserId())) + if (!has_replication_privilege(GetUserId()) + && !has_exclbackup_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser or replication role to run a backup")))); + errmsg("must be superuser, replication role or exclusive backup role to run a backup"))); stoppoint = do_pg_stop_backup(NULL, true, NULL); @@ -100,10 +103,10 @@ pg_switch_xlog(PG_FUNCTION_ARGS) { XLogRecPtr switchpoint; - if (!superuser()) + if (!has_exclbackup_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to switch transaction log files")))); + errmsg("must be superuser or exclusive backup role to switch transaction log files"))); if (RecoveryInProgress()) ereport(ERROR, @@ -129,10 +132,10 @@ pg_create_restore_point(PG_FUNCTION_ARGS) char *restore_name_str; XLogRecPtr restorepoint; - if (!superuser()) + if (!has_exclbackup_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to create a restore point")))); + (errmsg("must be superuser or exclusive backup role to create a restore point")))); if (RecoveryInProgress()) ereport(ERROR, @@ -338,10 +341,10 @@ pg_xlogfile_name(PG_FUNCTION_ARGS) Datum pg_xlog_replay_pause(PG_FUNCTION_ARGS) { - if (!superuser()) + if (!has_xlog_replay_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to control recovery")))); + (errmsg("must be superuser or xlog replay role to control recovery")))); if (!RecoveryInProgress()) ereport(ERROR, @@ -360,10 +363,10 @@ pg_xlog_replay_pause(PG_FUNCTION_ARGS) Datum pg_xlog_replay_resume(PG_FUNCTION_ARGS) { - if (!superuser()) + if (!has_xlog_replay_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to control recovery")))); + (errmsg("must be superuser or xlog replay role to control recovery")))); if (!RecoveryInProgress()) ereport(ERROR, diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 1e3888e..1d738d6 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -5080,6 +5080,31 @@ has_createrole_privilege(Oid roleid) return result; } +/* + * Check whether specified role has REPLICATION privilege + */ +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; +} + +/* + * Check whether specified role has BYPASSRLS privilege + */ bool has_bypassrls_privilege(Oid roleid) { @@ -5100,6 +5125,118 @@ has_bypassrls_privilege(Oid roleid) } /* + * Check whether specified role has EXCLUSIVEBACKUP privilege + */ +bool +has_exclbackup_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))->rolexclbackup; + ReleaseSysCache(utup); + } + + return result; +} + +/* + * Check whether specified role has XLOGREPLAY privilege + */ +bool +has_xlog_replay_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))->rolxlogreplay; + ReleaseSysCache(utup); + } + + return result; +} + +/* + * Check whether specified role has LOGFILE privilege + */ +bool +has_logfile_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))->rollogfile; + ReleaseSysCache(utup); + } + return result; +} + +/* + * Check whether specified role has MONITOR privilege + */ +bool +has_monitor_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))->rolmonitor; + ReleaseSysCache(utup); + } + return result; +} + +/* + * Check whether specified role has SIGNAL privilege + */ +bool +has_signal_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))->rolsignal; + ReleaseSysCache(utup); + } + return result; +} + +/* * Fetch pg_default_acl entry for given role, namespace and object type * (object type must be given in pg_default_acl's encoding). * Returns NULL if no such entry. diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 2210eed..e0eb5c3 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -55,15 +55,6 @@ static void DelRoleMems(const char *rolename, Oid roleid, 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 */ @@ -88,6 +79,11 @@ CreateRole(CreateRoleStmt *stmt) bool canlogin = false; /* Can this user login? */ bool isreplication = false; /* Is this a replication role? */ bool bypassrls = false; /* Is this a row security enabled role? */ + bool exclbackup = false; + bool xlogreplay = false; + bool logfile = false; + bool monitor = false; + bool signal = false; int connlimit = -1; /* maximum connections allowed */ List *addroleto = NIL; /* roles to make this a member of */ List *rolemembers = NIL; /* roles to be members of this role */ @@ -108,6 +104,11 @@ CreateRole(CreateRoleStmt *stmt) DefElem *dadminmembers = NULL; DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; + DefElem *dexclbackup = NULL; + DefElem *dxlogreplay = NULL; + DefElem *dlogfile = NULL; + DefElem *dmonitor = NULL; + DefElem *dsignal = NULL; /* The defaults can vary depending on the original statement type */ switch (stmt->stmt_type) @@ -242,6 +243,46 @@ CreateRole(CreateRoleStmt *stmt) errmsg("conflicting or redundant options"))); dbypassRLS = defel; } + else if (strcmp(defel->defname, "exclbackup") == 0) + { + if (dexclbackup) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dexclbackup = defel; + } + else if (strcmp(defel->defname, "xlogreplay") == 0) + { + if (dxlogreplay) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dxlogreplay = defel; + } + else if (strcmp(defel->defname, "logfile") == 0) + { + if (dlogfile) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dlogfile = defel; + } + else if (strcmp(defel->defname, "monitor") == 0) + { + if (dmonitor) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmonitor = defel; + } + else if (strcmp(defel->defname, "signal") == 0) + { + if (dsignal) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dsignal = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -279,6 +320,16 @@ CreateRole(CreateRoleStmt *stmt) validUntil = strVal(dvalidUntil->arg); if (dbypassRLS) bypassrls = intVal(dbypassRLS->arg) != 0; + if (dexclbackup) + exclbackup = intVal(dexclbackup->arg) != 0; + if (dxlogreplay) + xlogreplay = intVal(dxlogreplay->arg) != 0; + if (dlogfile) + logfile = intVal(dlogfile->arg) != 0; + if (dmonitor) + monitor = intVal(dmonitor->arg) != 0; + if (dsignal) + signal = intVal(dsignal->arg) != 0; /* Check some permissions first */ if (issuper) @@ -304,7 +355,7 @@ CreateRole(CreateRoleStmt *stmt) } else { - if (!have_createrole_privilege()) + if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create role"))); @@ -395,6 +446,11 @@ CreateRole(CreateRoleStmt *stmt) new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls); + new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup); + new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay); + new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile); + new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor); + new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal); tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); @@ -496,6 +552,11 @@ AlterRole(AlterRoleStmt *stmt) Datum validUntil_datum; /* same, as timestamptz Datum */ bool validUntil_null; bool bypassrls = -1; + bool exclbackup = -1; + bool xlogreplay = -1; + bool logfile = -1; + bool monitor = -1; + bool signal = -1; DefElem *dpassword = NULL; DefElem *dissuper = NULL; DefElem *dinherit = NULL; @@ -507,6 +568,11 @@ AlterRole(AlterRoleStmt *stmt) DefElem *drolemembers = NULL; DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; + DefElem *dexclbackup = NULL; + DefElem *dxlogreplay = NULL; + DefElem *dlogfile = NULL; + DefElem *dmonitor = NULL; + DefElem *dsignal = NULL; Oid roleid; /* Extract options from the statement node tree */ @@ -609,6 +675,46 @@ AlterRole(AlterRoleStmt *stmt) errmsg("conflicting or redundant options"))); dbypassRLS = defel; } + else if (strcmp(defel->defname, "exclbackup") == 0) + { + if (dexclbackup) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dexclbackup = defel; + } + else if (strcmp(defel->defname, "xlogreplay") == 0) + { + if (dxlogreplay) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dxlogreplay = defel; + } + else if (strcmp(defel->defname, "logfile") == 0) + { + if (dlogfile) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dlogfile = defel; + } + else if (strcmp(defel->defname, "monitor") == 0) + { + if (dmonitor) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dmonitor = defel; + } + else if (strcmp(defel->defname, "signal") == 0) + { + if (dsignal) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dsignal = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -642,6 +748,16 @@ AlterRole(AlterRoleStmt *stmt) validUntil = strVal(dvalidUntil->arg); if (dbypassRLS) bypassrls = intVal(dbypassRLS->arg); + if (dexclbackup) + exclbackup = intVal(dexclbackup->arg); + if (dxlogreplay) + xlogreplay = intVal(dxlogreplay->arg); + if (dlogfile) + logfile = intVal(dlogfile->arg); + if (dmonitor) + monitor = intVal(dmonitor->arg); + if (dsignal) + signal = intVal(dsignal->arg); /* * Scan the pg_authid relation to be certain the user exists. @@ -682,13 +798,18 @@ AlterRole(AlterRoleStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change bypassrls attribute"))); } - else if (!have_createrole_privilege()) + else if (!has_createrole_privilege(GetUserId())) { if (!(inherit < 0 && createrole < 0 && createdb < 0 && canlogin < 0 && isreplication < 0 && + exclbackup < 0 && + xlogreplay < 0 && + logfile < 0 && + monitor < 0 && + signal < 0 && !dconnlimit && !rolemembers && !validUntil && @@ -821,6 +942,36 @@ AlterRole(AlterRoleStmt *stmt) new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true; } + if (exclbackup >= 0) + { + new_record[Anum_pg_authid_rolexclbackup - 1] = BoolGetDatum(exclbackup > 0); + new_record_repl[Anum_pg_authid_rolexclbackup - 1] = true; + } + + if (xlogreplay >= 0) + { + new_record[Anum_pg_authid_rolxlogreplay - 1] = BoolGetDatum(xlogreplay > 0); + new_record_repl[Anum_pg_authid_rolxlogreplay - 1] = true; + } + + if (logfile >= 0) + { + new_record[Anum_pg_authid_rollogfile - 1] = BoolGetDatum(logfile > 0); + new_record_repl[Anum_pg_authid_rollogfile - 1] = true; + } + + if (monitor >= 0) + { + new_record[Anum_pg_authid_rolmonitor - 1] = BoolGetDatum(monitor > 0); + new_record_repl[Anum_pg_authid_rolmonitor - 1] = true; + } + + if (signal >= 0) + { + new_record[Anum_pg_authid_rolsignal - 1] = BoolGetDatum(signal > 0); + new_record_repl[Anum_pg_authid_rolsignal - 1] = true; + } + new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl); simple_heap_update(pg_authid_rel, &tuple->t_self, new_tuple); @@ -898,7 +1049,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt) } else { - if (!have_createrole_privilege() && + if (!has_createrole_privilege(GetUserId()) && HeapTupleGetOid(roletuple) != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -951,7 +1102,7 @@ DropRole(DropRoleStmt *stmt) pg_auth_members_rel; ListCell *item; - if (!have_createrole_privilege()) + if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to drop role"))); @@ -1182,7 +1333,7 @@ RenameRole(const char *oldname, const char *newname) } else { - if (!have_createrole_privilege()) + if (!has_createrole_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename role"))); @@ -1409,7 +1560,7 @@ AddRoleMems(const char *rolename, Oid roleid, } else { - if (!have_createrole_privilege() && + if (!has_createrole_privilege(GetUserId()) && !is_admin_of_role(grantorId, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -1555,7 +1706,7 @@ DelRoleMems(const char *rolename, Oid roleid, } else { - if (!have_createrole_privilege() && + if (!has_createrole_privilege(GetUserId()) && !is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 581f7a1..ead0ce2 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -978,6 +978,26 @@ AlterOptRoleElem: */ $$ = makeDefElem("inherit", (Node *)makeInteger(FALSE)); } + else if (strcmp($1, "exclusivebackup") == 0) + $$ = makeDefElem("exclbackup", (Node *)makeInteger(TRUE)); + else if (strcmp($1, "noexclusivebackup") == 0) + $$ = makeDefElem("exclbackup", (Node *)makeInteger(FALSE)); + else if (strcmp($1, "xlogreplay") == 0) + $$ = makeDefElem("xlogreplay", (Node *)makeInteger(TRUE)); + else if (strcmp($1, "noxlogreplay") == 0) + $$ = makeDefElem("xlogreplay", (Node *)makeInteger(FALSE)); + else if (strcmp($1, "logfile") == 0) + $$ = makeDefElem("logfile", (Node *)makeInteger(TRUE)); + else if (strcmp($1, "nologfile") == 0) + $$ = makeDefElem("logfile", (Node *)makeInteger(FALSE)); + else if (strcmp($1, "monitor") == 0) + $$ = makeDefElem("monitor", (Node *)makeInteger(TRUE)); + else if (strcmp($1, "nomonitor") == 0) + $$ = makeDefElem("monitor", (Node *)makeInteger(FALSE)); + else if (strcmp($1, "signal") == 0) + $$ = makeDefElem("signal", (Node *)makeInteger(TRUE)); + else if (strcmp($1, "nosignal") == 0) + $$ = makeDefElem("signal", (Node *)makeInteger(FALSE)); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c index 3be5263..b7e7947 100644 --- a/src/backend/replication/logical/logicalfuncs.c +++ b/src/backend/replication/logical/logicalfuncs.c @@ -29,6 +29,7 @@ #include "mb/pg_wchar.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/inval.h" @@ -202,15 +203,6 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count) } } -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. * @@ -324,7 +316,10 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin if (get_call_result_type(fcinfo, NULL, &p->tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); - check_permissions(); + if (!has_replication_privilege(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("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 index f31925d..35e1c11 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -20,18 +20,10 @@ #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" -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. @@ -51,7 +43,10 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS) if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); - check_permissions(); + if (!has_replication_privilege(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or replication role to use replication slots"))); CheckSlotRequirements(); @@ -94,7 +89,10 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS) if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); - check_permissions(); + if (!has_replication_privilege(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or replication role to use replication slots"))); CheckLogicalDecodingRequirements(); @@ -143,7 +141,10 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS) { Name name = PG_GETARG_NAME(0); - check_permissions(); + if (!has_replication_privilege(GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser or replication role to use replication slots"))); CheckSlotRequirements(); diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index af5c1cc..ee8b6f9 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -71,6 +71,7 @@ #include "storage/proc.h" #include "storage/procarray.h" #include "tcop/tcopprot.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -2794,11 +2795,12 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS) memset(nulls, 0, sizeof(nulls)); values[0] = Int32GetDatum(walsnd->pid); - if (!superuser()) + if (!has_monitor_privilege(GetUserId())) { /* - * Only superusers can see details. Other users only get the pid - * value to know it's a walsender, but no details. + * Only users with the MONITOR attribute or superuser privileges can + * see details. Other users only get the pid value to know it's a + * walsender, but no details. */ MemSet(&nulls[1], true, PG_STAT_GET_WAL_SENDERS_COLS - 1); } diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 29f7c3b..0c1c31a 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -37,6 +37,7 @@ #include "utils/lsyscache.h" #include "utils/ruleutils.h" #include "tcop/tcopprot.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/timestamp.h" @@ -113,7 +114,19 @@ pg_signal_backend(int pid, int sig) return SIGNAL_BACKEND_ERROR; } - if (!(superuser() || proc->roleId == GetUserId())) + /* + * If the current user is not a superuser, then they aren't allowed to + * signal backends which are owned by a superuser. + */ + if (!superuser() && superuser_arg(proc->roleId)) + return SIGNAL_BACKEND_NOPERMISSION; + + /* + * If the current user is not a member of the role owning the process and + * does not have the SIGNAL permission, then permission is denied. + */ + if (!has_privs_of_role(GetUserId(), proc->roleId) + && !has_signal_privilege(GetUserId())) return SIGNAL_BACKEND_NOPERMISSION; /* @@ -202,10 +215,10 @@ pg_reload_conf(PG_FUNCTION_ARGS) Datum pg_rotate_logfile(PG_FUNCTION_ARGS) { - if (!superuser()) + if (!has_logfile_privilege(GetUserId())) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to rotate log files")))); + errmsg("must be superuser or have logfile permission to rotate log files"))); if (!Logging_collector) { diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 9964c5e..aae4d0d 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -20,6 +20,7 @@ #include "libpq/ip.h" #include "miscadmin.h" #include "pgstat.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/inet.h" #include "utils/timestamp.h" @@ -626,6 +627,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) HeapTuple tuple; LocalPgBackendStatus *local_beentry; PgBackendStatus *beentry; + Oid current_user_id; MemSet(values, 0, sizeof(values)); MemSet(nulls, 0, sizeof(nulls)); @@ -675,8 +677,14 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) else nulls[15] = true; - /* Values only available to same user or superuser */ - if (superuser() || beentry->st_userid == GetUserId()) + /* + * Values only available to roles which are members of this role, + * or which have the MONITOR privilege. + */ + current_user_id = GetUserId(); + + if (has_monitor_privilege(current_user_id) + || has_privs_of_role(current_user_id, beentry->st_userid)) { SockAddr zero_clientaddr; @@ -875,10 +883,14 @@ pg_stat_get_backend_activity(PG_FUNCTION_ARGS) int32 beid = PG_GETARG_INT32(0); PgBackendStatus *beentry; const char *activity; + Oid current_user_id; + + current_user_id = GetUserId(); if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) activity = "<backend information not available>"; - else if (!superuser() && beentry->st_userid != GetUserId()) + else if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) activity = "<insufficient privilege>"; else if (*(beentry->st_activity) == '\0') activity = "<command string not enabled>"; @@ -895,11 +907,15 @@ pg_stat_get_backend_waiting(PG_FUNCTION_ARGS) int32 beid = PG_GETARG_INT32(0); bool result; PgBackendStatus *beentry; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); result = beentry->st_waiting; @@ -914,11 +930,15 @@ pg_stat_get_backend_activity_start(PG_FUNCTION_ARGS) int32 beid = PG_GETARG_INT32(0); TimestampTz result; PgBackendStatus *beentry; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); result = beentry->st_activity_start_timestamp; @@ -940,11 +960,15 @@ pg_stat_get_backend_xact_start(PG_FUNCTION_ARGS) int32 beid = PG_GETARG_INT32(0); TimestampTz result; PgBackendStatus *beentry; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); result = beentry->st_xact_start_timestamp; @@ -962,11 +986,15 @@ pg_stat_get_backend_start(PG_FUNCTION_ARGS) int32 beid = PG_GETARG_INT32(0); TimestampTz result; PgBackendStatus *beentry; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); result = beentry->st_proc_start_timestamp; @@ -986,11 +1014,15 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS) SockAddr zero_clientaddr; char remote_host[NI_MAXHOST]; int ret; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ @@ -1033,11 +1065,15 @@ pg_stat_get_backend_client_port(PG_FUNCTION_ARGS) SockAddr zero_clientaddr; char remote_port[NI_MAXSERV]; int ret; + Oid current_user_id; if ((beentry = pgstat_fetch_stat_beentry(beid)) == NULL) PG_RETURN_NULL(); - if (!superuser() && beentry->st_userid != GetUserId()) + current_user_id = GetUserId(); + + if (!has_monitor_privilege(current_user_id) + && !has_privs_of_role(current_user_id, beentry->st_userid)) PG_RETURN_NULL(); /* A zeroed client addr means we don't know */ diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 1dc3153..bb43b62 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -430,25 +430,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context) 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 */ diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 1e646a1..8cde310 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -765,7 +765,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, { Assert(!bootstrap); - if (!superuser() && !has_rolreplication(GetUserId())) + if (!has_replication_privilege(GetUserId())) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index b3f43e1..c2128b8 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -53,6 +53,11 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC bool rolcanlogin; /* allowed to log in as session user? */ bool rolreplication; /* role used for streaming replication */ bool rolbypassrls; /* allowed to bypass row level security? */ + bool rolexclbackup; /* allowed to peform backup operations? */ + bool rolxlogreplay; /* allowed to control xlog replay */ + bool rollogfile; /* allowed to rotate log files? */ + bool rolmonitor; /* allowed to view pg_stat_* details? */ + bool rolsignal; /* allowed to signal backed processes? */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */ /* remaining fields may be null; use heap_getattr to read them! */ @@ -76,7 +81,7 @@ typedef FormData_pg_authid *Form_pg_authid; * compiler constants for pg_authid * ---------------- */ -#define Natts_pg_authid 12 +#define Natts_pg_authid 17 #define Anum_pg_authid_rolname 1 #define Anum_pg_authid_rolsuper 2 #define Anum_pg_authid_rolinherit 3 @@ -86,9 +91,14 @@ typedef FormData_pg_authid *Form_pg_authid; #define Anum_pg_authid_rolcanlogin 7 #define Anum_pg_authid_rolreplication 8 #define Anum_pg_authid_rolbypassrls 9 -#define Anum_pg_authid_rolconnlimit 10 -#define Anum_pg_authid_rolpassword 11 -#define Anum_pg_authid_rolvaliduntil 12 +#define Anum_pg_authid_rolexclbackup 10 +#define Anum_pg_authid_rolxlogreplay 11 +#define Anum_pg_authid_rollogfile 12 +#define Anum_pg_authid_rolmonitor 13 +#define Anum_pg_authid_rolsignal 14 +#define Anum_pg_authid_rolconnlimit 15 +#define Anum_pg_authid_rolpassword 16 +#define Anum_pg_authid_rolvaliduntil 17 /* ---------------- * initial contents of pg_authid @@ -97,7 +107,7 @@ typedef FormData_pg_authid *Form_pg_authid; * user choices. * ---------------- */ -DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_)); +DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t t t t t t -1 _null_ _null_)); #define BOOTSTRAP_SUPERUSERID 10 diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index eacfccb..ec516d9 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -454,7 +454,6 @@ extern void ValidatePgVersion(const char *path); 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); diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index ab0df6c..63c702b 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -328,5 +328,11 @@ extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid); 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); +extern bool has_exclbackup_privilege(Oid roleid); +extern bool has_xlog_replay_privilege(Oid roleid); +extern bool has_logfile_privilege(Oid roleid); +extern bool has_monitor_privilege(Oid roleid); +extern bool has_signal_privilege(Oid roleid); #endif /* ACL_H */ -- 1.9.1
signature.asc
Description: Digital signature