From a53c2cdedaeaa786e9d9324a8154208f76a6c711 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Tue, 29 Jun 2021 09:54:48 -0700
Subject: [PATCH v4 2/5] Add default role for host security operations

Reducing the number of administrative tasks that require superuser
privileges by adding a new pg_host_security role and
delegating to it the authority to load extensions, and to create,
alter, and drop event triggers and tablespaces.
---
 doc/src/sgml/ref/alter_event_trigger.sgml   |  3 ++-
 doc/src/sgml/ref/create_event_trigger.sgml  |  3 ++-
 doc/src/sgml/ref/create_tablespace.sgml     |  7 +++---
 doc/src/sgml/ref/drop_tablespace.sgml       | 16 ++++++-------
 doc/src/sgml/ref/load.sgml                  | 12 +++++-----
 doc/src/sgml/user-manag.sgml                |  6 +++++
 src/backend/catalog/aclchk.c                | 19 +++++++++++-----
 src/backend/commands/event_trigger.c        | 11 +++++----
 src/backend/commands/extension.c            |  7 +++---
 src/backend/commands/tablespace.c           |  8 ++++---
 src/backend/tcop/utility.c                  |  4 +++-
 src/include/catalog/pg_authid.dat           |  5 +++++
 src/pl/plperl/expected/plperl_setup.out     |  4 ++--
 src/test/regress/expected/event_trigger.out |  4 ++--
 src/test/regress/expected/privileges.out    | 25 +++++++++++++++++++--
 src/test/regress/input/tablespace.source    | 10 +++++++++
 src/test/regress/output/tablespace.source   |  9 ++++++++
 src/test/regress/sql/privileges.sql         | 24 +++++++++++++++++++-
 18 files changed, 135 insertions(+), 42 deletions(-)

diff --git a/doc/src/sgml/ref/alter_event_trigger.sgml b/doc/src/sgml/ref/alter_event_trigger.sgml
index ef5253bf37..31ec520e13 100644
--- a/doc/src/sgml/ref/alter_event_trigger.sgml
+++ b/doc/src/sgml/ref/alter_event_trigger.sgml
@@ -37,7 +37,8 @@ ALTER EVENT TRIGGER <replaceable class="parameter">name</replaceable> RENAME TO
   </para>
 
   <para>
-   You must be superuser to alter an event trigger.
+   You must be superuser or a member of role
+   <literal>pg_host_security</literal> to alter an event trigger.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/create_event_trigger.sgml b/doc/src/sgml/ref/create_event_trigger.sgml
index becd31bcad..af21622a45 100644
--- a/doc/src/sgml/ref/create_event_trigger.sgml
+++ b/doc/src/sgml/ref/create_event_trigger.sgml
@@ -116,7 +116,8 @@ CREATE EVENT TRIGGER <replaceable class="parameter">name</replaceable>
   <title>Notes</title>
 
   <para>
-   Only superusers can create event triggers.
+   Only superusers and members of role <literal>pg_host_security</literal> can
+   create event triggers.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ref/create_tablespace.sgml b/doc/src/sgml/ref/create_tablespace.sgml
index 84fa7ee5e2..cf4997b22b 100644
--- a/doc/src/sgml/ref/create_tablespace.sgml
+++ b/doc/src/sgml/ref/create_tablespace.sgml
@@ -38,9 +38,10 @@ CREATE TABLESPACE <replaceable class="parameter">tablespace_name</replaceable>
   </para>
 
   <para>
-   A tablespace allows superusers to define an alternative location on
-   the file system where the data files containing database objects
-   (such as tables and indexes) can reside.
+   A tablespace allows superusers and members of role
+   <literal>pg_host_security</literal> to define an alternative location on the
+   file system where the data files containing database objects (such as tables
+   and indexes) can reside.
   </para>
 
   <para>
diff --git a/doc/src/sgml/ref/drop_tablespace.sgml b/doc/src/sgml/ref/drop_tablespace.sgml
index 047e4e0481..dad2e6ac4d 100644
--- a/doc/src/sgml/ref/drop_tablespace.sgml
+++ b/doc/src/sgml/ref/drop_tablespace.sgml
@@ -33,14 +33,14 @@ DROP TABLESPACE [ IF EXISTS ] <replaceable class="parameter">name</replaceable>
   </para>
 
   <para>
-   A tablespace can only be dropped by its owner or a superuser.
-   The tablespace must be empty of all database objects before it can be
-   dropped. It is possible that objects in other databases might still reside
-   in the tablespace even if no objects in the current database are using
-   the tablespace.  Also, if the tablespace is listed in the <xref
-   linkend="guc-temp-tablespaces"/> setting of any active session, the
-   <command>DROP</command> might fail due to temporary files residing in the
-   tablespace.
+   A tablespace can only be dropped by its owner, a member of role
+   <literal>pg_host_security</literal>, or a superuser.  The tablespace must be
+   empty of all database objects before it can be dropped. It is possible that
+   objects in other databases might still reside in the tablespace even if no
+   objects in the current database are using the tablespace.  Also, if the
+   tablespace is listed in the <xref linkend="guc-temp-tablespaces"/> setting
+   of any active session, the <command>DROP</command> might fail due to
+   temporary files residing in the tablespace.
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/load.sgml b/doc/src/sgml/ref/load.sgml
index 2c214dd726..e401c5b078 100644
--- a/doc/src/sgml/ref/load.sgml
+++ b/doc/src/sgml/ref/load.sgml
@@ -52,12 +52,12 @@ LOAD '<replaceable class="parameter">filename</replaceable>'
   </indexterm>
 
   <para>
-   Non-superusers can only apply <command>LOAD</command> to library files
-   located in <filename>$libdir/plugins/</filename> &mdash; the specified
-   <replaceable class="parameter">filename</replaceable> must begin
-   with exactly that string.  (It is the database administrator's
-   responsibility to ensure that only <quote>safe</quote> libraries
-   are installed there.)
+   Non-superusers not members of role <literal>pg_host_security</literal> can
+   only apply <command>LOAD</command> to library files located in
+   <filename>$libdir/plugins/</filename> &mdash; the specified <replaceable
+   class="parameter">filename</replaceable> must begin with exactly that
+   string.  (It is the database administrator's responsibility to ensure that
+   only <quote>safe</quote> libraries are installed there.)
   </para>
  </refsect1>
 
diff --git a/doc/src/sgml/user-manag.sgml b/doc/src/sgml/user-manag.sgml
index be74599b54..103c3ed21c 100644
--- a/doc/src/sgml/user-manag.sgml
+++ b/doc/src/sgml/user-manag.sgml
@@ -587,6 +587,12 @@ DROP ROLE doomed_role;
        subscriptions and publications, and connect to the server in logical
        replication mode.</entry>
       </row>
+      <row>
+       <entry>pg_host_security</entry>
+       <entry>Allow loading extensions, and creating, altering, and dropping
+       event triggers and tablespaces.
+       </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 53392414f1..50bafae58e 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -4299,8 +4299,12 @@ pg_tablespace_aclmask(Oid spc_oid, Oid roleid,
 	Acl		   *acl;
 	Oid			ownerId;
 
-	/* Superusers bypass all permission checking. */
-	if (superuser_arg(roleid))
+	/*
+	 * Superusers and members of the pg_host_security role bypass all
+	 * permission checking.
+	 */
+	if (superuser_arg(roleid) ||
+		has_privs_of_role(roleid, ROLE_PG_HOST_SECURITY))
 		return mask;
 
 	/*
@@ -5027,8 +5031,12 @@ pg_tablespace_ownercheck(Oid spc_oid, Oid roleid)
 	HeapTuple	spctuple;
 	Oid			spcowner;
 
-	/* Superusers bypass all permission checking. */
-	if (superuser_arg(roleid))
+	/*
+	 * Superusers and members of the pg_host_security role bypass all
+	 * permission checking.
+	 */
+	if (superuser_arg(roleid) ||
+		has_privs_of_role(roleid, ROLE_PG_HOST_SECURITY))
 		return true;
 
 	/* Search syscache for pg_tablespace */
@@ -5217,7 +5225,8 @@ pg_event_trigger_ownercheck(Oid et_oid, Oid roleid)
 	Oid			ownerId;
 
 	/* Superusers bypass all permission checking. */
-	if (superuser_arg(roleid))
+	if (superuser_arg(roleid) ||
+		has_privs_of_role(GetUserId(), ROLE_PG_HOST_SECURITY))
 		return true;
 
 	tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid));
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 9c31c9e763..860565f825 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -20,6 +20,7 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_event_trigger.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -119,12 +120,13 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	 * this, but there are obvious privilege escalation risks which would have
 	 * to somehow be plugged first.
 	 */
-	if (!superuser())
+	if (!superuser() &&
+		!has_privs_of_role(GetUserId(), ROLE_PG_HOST_SECURITY))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to create event trigger \"%s\"",
 						stmt->trigname),
-				 errhint("Must be superuser to create an event trigger.")));
+				 errhint("Must be superuser or a member of the pg_host_security role to create an event trigger.")));
 
 	/* Validate event name. */
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
@@ -477,12 +479,13 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
 					   NameStr(form->evtname));
 
 	/* New owner must be a superuser */
-	if (!superuser_arg(newOwnerId))
+	if (!superuser_arg(newOwnerId) &&
+		!has_privs_of_role(newOwnerId, ROLE_PG_HOST_SECURITY))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to change owner of event trigger \"%s\"",
 						NameStr(form->evtname)),
-				 errhint("The owner of an event trigger must be a superuser.")));
+				 errhint("The owner of an event trigger must be a superuser or a member of the pg_host_security role.")));
 
 	form->evtowner = newOwnerId;
 	CatalogTupleUpdate(rel, &tup->t_self, tup);
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 41857feda9..db22f3207f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -852,7 +852,8 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 	 * here so that the control flags are correctly associated with the right
 	 * script(s) if they happen to be set in secondary control files.
 	 */
-	if (control->superuser && !superuser())
+	if (control->superuser && !superuser() &&
+		!has_privs_of_role(GetUserId(), ROLE_PG_HOST_SECURITY))
 	{
 		if (extension_is_trusted(control))
 			switch_to_superuser = true;
@@ -863,7 +864,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 							control->name),
 					 control->trusted
 					 ? errhint("Must have CREATE privilege on current database to create this extension.")
-					 : errhint("Must be superuser to create this extension.")));
+					 : errhint("Must be superuser or a member of the pg_host_security role to create this extension.")));
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -871,7 +872,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 							control->name),
 					 control->trusted
 					 ? errhint("Must have CREATE privilege on current database to update this extension.")
-					 : errhint("Must be superuser to update this extension.")));
+					 : errhint("Must be superuser or a member of the pg_host_security role to update this extension.")));
 	}
 
 	filename = get_extension_script_filename(control, from_version, version);
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 69ea155d50..c8720664b6 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -63,6 +63,7 @@
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_tablespace.h"
 #include "commands/comment.h"
@@ -242,13 +243,14 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
 	Oid			ownerId;
 	Datum		newOptions;
 
-	/* Must be super user */
-	if (!superuser())
+	/* Must be super user or a member of the pg_host_security role */
+	if (!superuser() &&
+		!has_privs_of_role(GetUserId(), ROLE_PG_HOST_SECURITY))
 		ereport(ERROR,
 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 				 errmsg("permission denied to create tablespace \"%s\"",
 						stmt->tablespacename),
-				 errhint("Must be superuser to create a tablespace.")));
+				 errhint("Must be superuser or a member of the pg_host_security role to create a tablespace.")));
 
 	/* However, the eventual owner of the tablespace need not be */
 	if (stmt->owner)
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 7a2da9dab4..0e2278b049 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -24,6 +24,7 @@
 #include "catalog/catalog.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/toasting.h"
 #include "commands/alter.h"
@@ -825,7 +826,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 				closeAllVfds(); /* probably not necessary... */
 				/* Allowed names are restricted if you're not superuser */
-				load_file(stmt->filename, !superuser());
+				load_file(stmt->filename, !superuser() &&
+						  !has_privs_of_role(GetUserId(), ROLE_PG_HOST_SECURITY));
 			}
 			break;
 
diff --git a/src/include/catalog/pg_authid.dat b/src/include/catalog/pg_authid.dat
index 0048a5fab7..33d5b637a1 100644
--- a/src/include/catalog/pg_authid.dat
+++ b/src/include/catalog/pg_authid.dat
@@ -84,5 +84,10 @@
   rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
   rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
   rolpassword => '_null_', rolvaliduntil => '_null_' },
+{ oid => '9277', oid_symbol => 'ROLE_PG_HOST_SECURITY',
+  rolname => 'pg_host_security', rolsuper => 'f', rolinherit => 't',
+  rolcreaterole => 'f', rolcreatedb => 'f', rolcanlogin => 'f',
+  rolreplication => 'f', rolbypassrls => 'f', rolconnlimit => '-1',
+  rolpassword => '_null_', rolvaliduntil => '_null_' },
 
 ]
diff --git a/src/pl/plperl/expected/plperl_setup.out b/src/pl/plperl/expected/plperl_setup.out
index a1a24dfb41..bab9b3db53 100644
--- a/src/pl/plperl/expected/plperl_setup.out
+++ b/src/pl/plperl/expected/plperl_setup.out
@@ -12,7 +12,7 @@ ERROR:  permission denied to create extension "plperl"
 HINT:  Must have CREATE privilege on current database to create this extension.
 CREATE EXTENSION plperlu;  -- fail
 ERROR:  permission denied to create extension "plperlu"
-HINT:  Must be superuser to create this extension.
+HINT:  Must be superuser or a member of the pg_host_security role to create this extension.
 RESET ROLE;
 DO $$
 begin
@@ -24,7 +24,7 @@ SET ROLE regress_user1;
 CREATE EXTENSION plperl;
 CREATE EXTENSION plperlu;  -- fail
 ERROR:  permission denied to create extension "plperlu"
-HINT:  Must be superuser to create this extension.
+HINT:  Must be superuser or a member of the pg_host_security role to create this extension.
 CREATE FUNCTION foo1() returns int language plperl as '1;';
 SELECT foo1();
  foo1 
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 44d545de25..709532c06a 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -90,7 +90,7 @@ set role regress_evt_user;
 create event trigger regress_event_trigger_noperms on ddl_command_start
    execute procedure test_event_trigger();
 ERROR:  permission denied to create event trigger "regress_event_trigger_noperms"
-HINT:  Must be superuser to create an event trigger.
+HINT:  Must be superuser or a member of the pg_host_security role to create an event trigger.
 reset role;
 -- test enabling and disabling
 alter event trigger regress_event_trigger disable;
@@ -176,7 +176,7 @@ NOTICE:  test_event_trigger: ddl_command_end ALTER DEFAULT PRIVILEGES
 -- alter owner to non-superuser should fail
 alter event trigger regress_event_trigger owner to regress_evt_user;
 ERROR:  permission denied to change owner of event trigger "regress_event_trigger"
-HINT:  The owner of an event trigger must be a superuser.
+HINT:  The owner of an event trigger must be a superuser or a member of the pg_host_security role.
 -- alter owner to superuser should work
 alter role regress_evt_user superuser;
 alter event trigger regress_event_trigger owner to regress_evt_user;
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 83cff902f3..9664276359 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -13,6 +13,7 @@ DROP ROLE IF EXISTS regress_priv_user4;
 DROP ROLE IF EXISTS regress_priv_user5;
 DROP ROLE IF EXISTS regress_priv_user6;
 DROP ROLE IF EXISTS regress_priv_user7;
+DROP ROLE IF EXISTS regress_priv_user8;
 SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid;
  lo_unlink 
 -----------
@@ -29,6 +30,7 @@ CREATE USER regress_priv_user5;	-- duplicate
 ERROR:  role "regress_priv_user5" already exists
 CREATE USER regress_priv_user6;
 CREATE USER regress_priv_user7;
+CREATE USER regress_priv_user8;
 GRANT pg_read_all_data TO regress_priv_user6;
 GRANT pg_write_all_data TO regress_priv_user7;
 CREATE GROUP regress_priv_group1;
@@ -1829,6 +1831,24 @@ TABLE information_schema.enabled_roles;
 INSERT INTO datdba_only DEFAULT VALUES;
 ERROR:  permission denied for table datdba_only
 ROLLBACK;
+-- test pg_host_security authority to create event triggers
+GRANT pg_host_security TO regress_priv_user8;
+SET SESSION AUTHORIZATION regress_priv_user8;
+CREATE FUNCTION user8_event_trigger() RETURNS event_trigger AS $$
+BEGIN
+    RAISE DEBUG 'user8_event_trigger: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE PLPGSQL;
+CREATE EVENT TRIGGER user8_event_trigger_start ON ddl_command_start
+   EXECUTE PROCEDURE user8_event_trigger();
+CREATE EVENT TRIGGER user8_event_trigger_end ON ddl_command_end
+   EXECUTE FUNCTION user8_event_trigger();
+COMMENT ON EVENT TRIGGER user8_event_trigger_start IS 'test comment';
+ALTER EVENT TRIGGER user8_event_trigger_end RENAME TO user8_event_trigger_finish;
+DROP EVENT TRIGGER user8_event_trigger_start;
+DROP EVENT TRIGGER user8_event_trigger_finish;
+DROP FUNCTION user8_event_trigger;
+RESET SESSION AUTHORIZATION;
 -- test default ACLs
 \c -
 CREATE SCHEMA testns;
@@ -2328,8 +2348,9 @@ DROP USER regress_priv_user4;
 DROP USER regress_priv_user5;
 DROP USER regress_priv_user6;
 DROP USER regress_priv_user7;
-DROP USER regress_priv_user8; -- does not exist
-ERROR:  role "regress_priv_user8" does not exist
+DROP USER regress_priv_user8;
+DROP USER regress_priv_user9; -- does not exist
+ERROR:  role "regress_priv_user9" does not exist
 -- permissions with LOCK TABLE
 CREATE USER regress_locktable_user;
 CREATE TABLE lock_table (a int);
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index c133e73499..a46cb7fcbf 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -405,6 +405,16 @@ ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default
 -- Should succeed
 DROP TABLESPACE regress_tblspace_renamed;
 
+-- Role pg_host_security should be able to create, alter, and drop tablespaces
+CREATE USER tablespace_admin;
+GRANT pg_host_security TO tablespace_admin;
+SET SESSION AUTHORIZATION tablespace_admin;
+CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@'; -- ok
+ALTER TABLESPACE regress_tblspace RENAME TO regress_tblspace_renamed;
+ALTER TABLESPACE regress_tblspace_renamed SET (random_page_cost = 1.0, seq_page_cost = 1.1);
+DROP TABLESPACE regress_tblspace_renamed;
+RESET SESSION AUTHORIZATION;
+
 DROP SCHEMA testschema CASCADE;
 
 DROP ROLE regress_tablespace_user1;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 1bbe7e0323..03367118f0 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -924,6 +924,15 @@ ALTER TABLE ALL IN TABLESPACE regress_tblspace_renamed SET TABLESPACE pg_default
 NOTICE:  no matching relations in tablespace "regress_tblspace_renamed" found
 -- Should succeed
 DROP TABLESPACE regress_tblspace_renamed;
+-- Role pg_host_security should be able to create, alter, and drop tablespaces
+CREATE USER tablespace_admin;
+GRANT pg_host_security TO tablespace_admin;
+SET SESSION AUTHORIZATION tablespace_admin;
+CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@'; -- ok
+ALTER TABLESPACE regress_tblspace RENAME TO regress_tblspace_renamed;
+ALTER TABLESPACE regress_tblspace_renamed SET (random_page_cost = 1.0, seq_page_cost = 1.1);
+DROP TABLESPACE regress_tblspace_renamed;
+RESET SESSION AUTHORIZATION;
 DROP SCHEMA testschema CASCADE;
 NOTICE:  drop cascades to 6 other objects
 DETAIL:  drop cascades to table testschema.foo
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 3d1a1db987..aea13448bc 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -17,6 +17,7 @@ DROP ROLE IF EXISTS regress_priv_user4;
 DROP ROLE IF EXISTS regress_priv_user5;
 DROP ROLE IF EXISTS regress_priv_user6;
 DROP ROLE IF EXISTS regress_priv_user7;
+DROP ROLE IF EXISTS regress_priv_user8;
 
 SELECT lo_unlink(oid) FROM pg_largeobject_metadata WHERE oid >= 1000 AND oid < 3000 ORDER BY oid;
 
@@ -32,6 +33,7 @@ CREATE USER regress_priv_user5;
 CREATE USER regress_priv_user5;	-- duplicate
 CREATE USER regress_priv_user6;
 CREATE USER regress_priv_user7;
+CREATE USER regress_priv_user8;
 
 GRANT pg_read_all_data TO regress_priv_user6;
 GRANT pg_write_all_data TO regress_priv_user7;
@@ -1092,6 +1094,25 @@ TABLE information_schema.enabled_roles;
 INSERT INTO datdba_only DEFAULT VALUES;
 ROLLBACK;
 
+-- test pg_host_security authority to create event triggers
+GRANT pg_host_security TO regress_priv_user8;
+SET SESSION AUTHORIZATION regress_priv_user8;
+CREATE FUNCTION user8_event_trigger() RETURNS event_trigger AS $$
+BEGIN
+    RAISE DEBUG 'user8_event_trigger: % %', tg_event, tg_tag;
+END
+$$ LANGUAGE PLPGSQL;
+CREATE EVENT TRIGGER user8_event_trigger_start ON ddl_command_start
+   EXECUTE PROCEDURE user8_event_trigger();
+CREATE EVENT TRIGGER user8_event_trigger_end ON ddl_command_end
+   EXECUTE FUNCTION user8_event_trigger();
+COMMENT ON EVENT TRIGGER user8_event_trigger_start IS 'test comment';
+ALTER EVENT TRIGGER user8_event_trigger_end RENAME TO user8_event_trigger_finish;
+DROP EVENT TRIGGER user8_event_trigger_start;
+DROP EVENT TRIGGER user8_event_trigger_finish;
+DROP FUNCTION user8_event_trigger;
+RESET SESSION AUTHORIZATION;
+
 -- test default ACLs
 \c -
 
@@ -1391,7 +1412,8 @@ DROP USER regress_priv_user4;
 DROP USER regress_priv_user5;
 DROP USER regress_priv_user6;
 DROP USER regress_priv_user7;
-DROP USER regress_priv_user8; -- does not exist
+DROP USER regress_priv_user8;
+DROP USER regress_priv_user9; -- does not exist
 
 
 -- permissions with LOCK TABLE
-- 
2.21.1 (Apple Git-122.3)

