From ca6bada86f76943e732db66aee9cf02c565d22f8 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 20 Oct 2021 07:52:49 -0700
Subject: [PATCH v1 11/12] Adding role pg_manage_recovery_settings

This new role can SET or ALTER SYSTEM SET any GUC variable which
controls crash recovery settings.
---
 doc/src/sgml/user-manag.sgml            | 13 ++--
 src/backend/utils/misc/guc.c            | 20 +++---
 src/include/catalog/pg_authid.dat       |  5 ++
 src/include/utils/guc.h                 |  1 +
 src/test/regress/expected/guc_privs.out | 89 +++++++++++++++++++++++++
 src/test/regress/sql/guc_privs.sql      | 59 ++++++++++++++++
 6 files changed, 175 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index 548ece3acb..c97350ee92 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -618,6 +618,10 @@ DROP ROLE doomed_role;
        <entry>pg_manage_stats_settings</entry>
        <entry>Allow changing settings which control collection of statistics.</entry>
       </row>
+      <row>
+       <entry>pg_manage_recovery_settings</entry>
+       <entry>Allow changing settings which control or are generally used for recovery.</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
@@ -670,10 +674,11 @@ DROP ROLE doomed_role;
   <literal>pg_manage_replication_settings</literal>,
   <literal>pg_manage_connection_settings</literal>,
   <literal>pg_manage_wal_settings</literal>,
-  <literal>pg_manage_query_tuning_settings</literal> and
-  <literal>pg_manage_stats_settings</literal> roles are intended to
-  allow administrators to have trusted, but non-superuser, roles to configure
-  the server.
+  <literal>pg_manage_query_tuning_settings</literal>,
+  <literal>pg_manage_stats_settings</literal> and
+  <literal>pg_manage_recovery_settings</literal> roles are intended to allow
+  administrators to have trusted, but non-superuser, roles to configure the
+  server.
   </para>
 
   <para>
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a676c4af09..5e52b5544d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1287,7 +1287,7 @@ static struct config_bool ConfigureNamesBool[] =
 						 "(but still report a warning), and continue processing. This "
 						 "behavior could cause crashes or other serious problems. Only "
 						 "has an effect if checksums are enabled."),
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&ignore_checksum_failure,
@@ -1302,7 +1302,7 @@ static struct config_bool ConfigureNamesBool[] =
 						 "zero_damaged_pages to true causes the system to instead report a "
 						 "warning, zero out the damaged page, and continue processing. This "
 						 "behavior will destroy data, namely all the rows on the damaged page."),
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&zero_damaged_pages,
@@ -1322,7 +1322,7 @@ static struct config_bool ConfigureNamesBool[] =
 						 "crashes, data loss, propagate or hide corruption, "
 						 "or other serious problems. Only has an effect "
 						 "during recovery or in standby mode."),
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&ignore_invalid_pages,
@@ -1447,7 +1447,7 @@ static struct config_bool ConfigureNamesBool[] =
 		{"restart_after_crash", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
 			gettext_noop("Reinitialize server after backend crash."),
 			NULL,
-			GUC_SUPERUSER_ONLY
+			GUC_MANAGE_RECOVERY_SETTINGS
 		},
 		&restart_after_crash,
 		true,
@@ -1457,7 +1457,7 @@ static struct config_bool ConfigureNamesBool[] =
 		{"remove_temp_files_after_crash", PGC_SIGHUP, DEVELOPER_OPTIONS,
 			gettext_noop("Remove temporary files after backend crash."),
 			NULL,
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&remove_temp_files_after_crash,
@@ -2032,7 +2032,7 @@ static struct config_bool ConfigureNamesBool[] =
 		{"allow_system_table_mods", PGC_SUSET, DEVELOPER_OPTIONS,
 			gettext_noop("Allows modifications of the structure of system tables."),
 			NULL,
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&allowSystemTableMods,
@@ -2045,7 +2045,7 @@ static struct config_bool ConfigureNamesBool[] =
 			gettext_noop("Disables reading from system indexes."),
 			gettext_noop("It does not prevent updating the indexes, so it is safe "
 						 "to use.  The worst consequence is slowness."),
-			GUC_SUPERUSER_ONLY,
+			GUC_MANAGE_RECOVERY_SETTINGS,
 			GUC_NOT_IN_SAMPLE
 		},
 		&IgnoreSystemIndexes,
@@ -5337,7 +5337,7 @@ static struct config_enum ConfigureNamesEnum[] =
 		{"recovery_init_sync_method", PGC_SIGHUP, ERROR_HANDLING_OPTIONS,
 			gettext_noop("Sets the method for synchronizing the data directory before crash recovery."),
 			NULL,
-			GUC_MANAGE_LOGGING_SETTINGS | GUC_WRITE_SERVER_FILES
+			GUC_MANAGE_RECOVERY_SETTINGS
 		},
 		&recovery_init_sync_method,
 		RECOVERY_INIT_SYNC_METHOD_FSYNC, recovery_init_sync_method_options,
@@ -7667,6 +7667,10 @@ role_has_privileges(Oid roleid, int privileges)
 		! has_privs_of_role(roleid, ROLE_PG_MANAGE_STATS_SETTINGS))
 		return false;
 
+	if ((privileges & GUC_MANAGE_RECOVERY_SETTINGS) &&
+		! has_privs_of_role(roleid, ROLE_PG_MANAGE_RECOVERY_SETTINGS))
+		return false;
+
 	return true;
 }
 
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
index 3f80cbd59b..05ac06ccc9 100644
--- a/src/include/catalog/pg_authid.dat
+++ b/src/include/catalog/pg_authid.dat
@@ -124,5 +124,10 @@
   rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
   rolreplication => 'f', rolbypassrls => 't', rolconnlimit => '-1',
   rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '9288', oid_symbol => 'ROLE_PG_MANAGE_RECOVERY_SETTINGS',
+  rolname => 'pg_manage_recovery_settings', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 't', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },
 
 ]
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 3e91f13cb8..fe1e4b0c4c 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -211,6 +211,7 @@ typedef enum
 #define GUC_MANAGE_WAL_SETTINGS				0x0080
 #define GUC_MANAGE_QUERY_TUNING_SETTINGS	0x0200	/* 0x0100 = superuser */
 #define GUC_MANAGE_STATS_SETTINGS			0x0400
+#define GUC_MANAGE_RECOVERY_SETTINGS		0x0800
 
 /*
  * bit values in "flags" of a GUC variable
diff --git a/src/test/regress/expected/guc_privs.out b/src/test/regress/expected/guc_privs.out
index 4b8d50d93c..91f28d4b75 100644
--- a/src/test/regress/expected/guc_privs.out
+++ b/src/test/regress/expected/guc_privs.out
@@ -1009,3 +1009,92 @@ ERROR:  parameter "ssl_renegotiation_limit" cannot be changed
 RESET statement_timeout;
 RESET SESSION AUTHORIZATION;
 DROP ROLE regress_stats_full_admin;
+-- Test role pg_manage_recovery_settings
+-- Non-superuser with privileges to configure recovery
+CREATE ROLE regress_recovery_admin NOSUPERUSER;
+GRANT pg_manage_recovery_settings TO regress_recovery_admin;
+-- Perform all operations as user 'regress_recovery_admin' --
+SET SESSION AUTHORIZATION regress_recovery_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF;  -- fail, cannot be set after connection start
+ERROR:  parameter "ignore_system_indexes" cannot be set after connection start
+RESET ignore_system_indexes;  -- fail, cannot be set after connection start
+ERROR:  parameter "ignore_system_indexes" cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF;  -- ok
+ALTER SYSTEM RESET ignore_system_indexes;  -- ok
+ALTER SYSTEM SET post_auth_delay = 50;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "post_auth_delay"
+ALTER SYSTEM RESET post_auth_delay;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "post_auth_delay"
+-- PGC_INTERNAL
+SET block_size = 50;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+RESET block_size;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+ALTER SYSTEM SET block_size = 50;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+ALTER SYSTEM RESET block_size;  -- fail, cannot be changed
+ERROR:  parameter "block_size" cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000;  -- fail, requires restart
+ERROR:  parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+RESET autovacuum_freeze_max_age;  -- fail, requires restart
+ERROR:  parameter "autovacuum_freeze_max_age" cannot be changed without restarting the server
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum_freeze_max_age"
+ALTER SYSTEM RESET autovacuum_freeze_max_age;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum_freeze_max_age"
+ALTER SYSTEM SET ignore_invalid_pages = OFF;  -- ok
+ALTER SYSTEM RESET ignore_invalid_pages;  -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf';  -- fail, cannot be changed
+ERROR:  parameter "config_file" cannot be changed
+ALTER SYSTEM RESET config_file;  -- fail, cannot be changed
+ERROR:  parameter "config_file" cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF;  -- fail, requires reload
+ERROR:  parameter "autovacuum" cannot be changed now
+RESET autovacuum;  -- fail, requires reload
+ERROR:  parameter "autovacuum" cannot be changed now
+ALTER SYSTEM SET autovacuum = OFF;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum"
+ALTER SYSTEM RESET autovacuum;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "autovacuum"
+ALTER SYSTEM SET remove_temp_files_after_crash = OFF;  -- ok
+ALTER SYSTEM RESET remove_temp_files_after_crash;  -- ok
+-- PGC_SUSET
+SET lc_messages = 'en_US.UTF-8';  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+RESET lc_messages;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8';  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+ALTER SYSTEM RESET lc_messages;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "lc_messages"
+SET allow_system_table_mods = OFF;  -- ok
+RESET allow_system_table_mods;  -- ok
+ALTER SYSTEM SET allow_system_table_mods = OFF;  -- ok
+ALTER SYSTEM RESET allow_system_table_mods;  -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF;  -- fail, cannot be set after connection start
+ERROR:  parameter "jit_debugging_support" cannot be set after connection start
+RESET jit_debugging_support;  -- fail, cannot be set after connection start
+ERROR:  parameter "jit_debugging_support" cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "jit_debugging_support"
+ALTER SYSTEM RESET jit_debugging_support;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "jit_debugging_support"
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY';  -- ok
+RESET DateStyle;  -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY';  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "datestyle"
+ALTER SYSTEM RESET DateStyle;  -- fail, regress_recovery_admin has insufficient privileges
+ERROR:  permission denied to set parameter "datestyle"
+ALTER SYSTEM SET ssl_renegotiation_limit = 0;  -- fail, cannot be changed
+ERROR:  parameter "ssl_renegotiation_limit" cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit;  -- fail, cannot be changed
+ERROR:  parameter "ssl_renegotiation_limit" cannot be changed
+-- Finished testing role pg_manage_recovery_settings
+RESET statement_timeout;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_recovery_admin;
diff --git a/src/test/regress/sql/guc_privs.sql b/src/test/regress/sql/guc_privs.sql
index 78b6df76cf..bb25b00e2a 100644
--- a/src/test/regress/sql/guc_privs.sql
+++ b/src/test/regress/sql/guc_privs.sql
@@ -663,3 +663,62 @@ ALTER SYSTEM RESET ssl_renegotiation_limit;  -- fail, cannot be changed
 RESET statement_timeout;
 RESET SESSION AUTHORIZATION;
 DROP ROLE regress_stats_full_admin;
+-- Test role pg_manage_recovery_settings
+-- Non-superuser with privileges to configure recovery
+CREATE ROLE regress_recovery_admin NOSUPERUSER;
+GRANT pg_manage_recovery_settings TO regress_recovery_admin;
+-- Perform all operations as user 'regress_recovery_admin' --
+SET SESSION AUTHORIZATION regress_recovery_admin;
+-- PGC_BACKEND
+SET ignore_system_indexes = OFF;  -- fail, cannot be set after connection start
+RESET ignore_system_indexes;  -- fail, cannot be set after connection start
+ALTER SYSTEM SET ignore_system_indexes = OFF;  -- ok
+ALTER SYSTEM RESET ignore_system_indexes;  -- ok
+ALTER SYSTEM SET post_auth_delay = 50;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET post_auth_delay;  -- fail, regress_recovery_admin has insufficient privileges
+-- PGC_INTERNAL
+SET block_size = 50;  -- fail, cannot be changed
+RESET block_size;  -- fail, cannot be changed
+ALTER SYSTEM SET block_size = 50;  -- fail, cannot be changed
+ALTER SYSTEM RESET block_size;  -- fail, cannot be changed
+-- PGC_POSTMASTER
+SET autovacuum_freeze_max_age = 1000050000;  -- fail, requires restart
+RESET autovacuum_freeze_max_age;  -- fail, requires restart
+ALTER SYSTEM SET autovacuum_freeze_max_age = 1000050000;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET autovacuum_freeze_max_age;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM SET ignore_invalid_pages = OFF;  -- ok
+ALTER SYSTEM RESET ignore_invalid_pages;  -- ok
+ALTER SYSTEM SET config_file = '/usr/local/data/postgresql.conf';  -- fail, cannot be changed
+ALTER SYSTEM RESET config_file;  -- fail, cannot be changed
+-- PGC_SIGHUP
+SET autovacuum = OFF;  -- fail, requires reload
+RESET autovacuum;  -- fail, requires reload
+ALTER SYSTEM SET autovacuum = OFF;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET autovacuum;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM SET remove_temp_files_after_crash = OFF;  -- ok
+ALTER SYSTEM RESET remove_temp_files_after_crash;  -- ok
+-- PGC_SUSET
+SET lc_messages = 'en_US.UTF-8';  -- fail, regress_recovery_admin has insufficient privileges
+RESET lc_messages;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM SET lc_messages = 'en_US.UTF-8';  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET lc_messages;  -- fail, regress_recovery_admin has insufficient privileges
+SET allow_system_table_mods = OFF;  -- ok
+RESET allow_system_table_mods;  -- ok
+ALTER SYSTEM SET allow_system_table_mods = OFF;  -- ok
+ALTER SYSTEM RESET allow_system_table_mods;  -- ok
+-- PGC_SU_BACKEND
+SET jit_debugging_support = OFF;  -- fail, cannot be set after connection start
+RESET jit_debugging_support;  -- fail, cannot be set after connection start
+ALTER SYSTEM SET jit_debugging_support = OFF;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET jit_debugging_support;  -- fail, regress_recovery_admin has insufficient privileges
+-- PGC_USERSET
+SET DateStyle = 'ISO, MDY';  -- ok
+RESET DateStyle;  -- ok
+ALTER SYSTEM SET DateStyle = 'ISO, MDY';  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM RESET DateStyle;  -- fail, regress_recovery_admin has insufficient privileges
+ALTER SYSTEM SET ssl_renegotiation_limit = 0;  -- fail, cannot be changed
+ALTER SYSTEM RESET ssl_renegotiation_limit;  -- fail, cannot be changed
+-- Finished testing role pg_manage_recovery_settings
+RESET statement_timeout;
+RESET SESSION AUTHORIZATION;
+DROP ROLE regress_recovery_admin;
-- 
2.21.1 (Apple Git-122.3)

