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

Attachment: signature.asc
Description: Digital signature

Reply via email to