From 4019f20cf0a62c66ccf26461bcb38720fc083386 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Thu, 17 Mar 2022 19:30:09 -0700
Subject: [PATCH v2 1/2] Add String object access hooks

The first user of these will be the GUC access controls

Written by Joshua Brindle; refactored by Mark Dilger
---
 src/backend/catalog/objectaccess.c | 128 +++++++++++++++++++++++++++++
 src/backend/utils/misc/guc.c       |  17 ++++
 src/include/catalog/objectaccess.h |  70 +++++++++++++++-
 src/include/nodes/parsenodes.h     |   4 +-
 src/include/utils/acl.h            |   4 +-
 5 files changed, 220 insertions(+), 3 deletions(-)

diff --git a/src/backend/catalog/objectaccess.c b/src/backend/catalog/objectaccess.c
index 549fac4539..72ad6e9a90 100644
--- a/src/backend/catalog/objectaccess.c
+++ b/src/backend/catalog/objectaccess.c
@@ -20,6 +20,8 @@
  * and logging plugins.
  */
 object_access_hook_type object_access_hook = NULL;
+object_access_hook_type_str object_access_hook_str = NULL;
+
 
 /*
  * RunObjectPostCreateHook
@@ -143,3 +145,129 @@ RunFunctionExecuteHook(Oid objectId)
 						   ProcedureRelationId, objectId, 0,
 						   NULL);
 }
+
+/* String versions */
+
+
+/*
+ * RunObjectPostCreateHook
+ *
+ * It is entrypoint of OAT_POST_CREATE event
+ */
+void
+RunObjectPostCreateHookStr(Oid classId, const char *objectName, int subId,
+						bool is_internal)
+{
+	ObjectAccessPostCreate pc_arg;
+
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	memset(&pc_arg, 0, sizeof(ObjectAccessPostCreate));
+	pc_arg.is_internal = is_internal;
+
+	(*object_access_hook_str) (OAT_POST_CREATE,
+						   classId, objectName, subId,
+						   (void *) &pc_arg);
+}
+
+/*
+ * RunObjectDropHook
+ *
+ * It is entrypoint of OAT_DROP event
+ */
+void
+RunObjectDropHookStr(Oid classId, const char *objectName, int subId,
+				  int dropflags)
+{
+	ObjectAccessDrop drop_arg;
+
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	memset(&drop_arg, 0, sizeof(ObjectAccessDrop));
+	drop_arg.dropflags = dropflags;
+
+	(*object_access_hook_str) (OAT_DROP,
+						   classId, objectName, subId,
+						   (void *) &drop_arg);
+}
+
+/*
+ * RunObjectTruncateHook
+ *
+ * It is the entrypoint of OAT_TRUNCATE event
+ */
+void
+RunObjectTruncateHookStr(const char *objectName)
+{
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	(*object_access_hook_str) (OAT_TRUNCATE,
+						   RelationRelationId, objectName, 0,
+						   NULL);
+}
+
+/*
+ * RunObjectPostAlterHook
+ *
+ * It is entrypoint of OAT_POST_ALTER event
+ */
+void
+RunObjectPostAlterHookStr(Oid classId, const char *objectName, int subId,
+					   Oid auxiliaryId, bool is_internal)
+{
+	ObjectAccessPostAlter pa_arg;
+
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	memset(&pa_arg, 0, sizeof(ObjectAccessPostAlter));
+	pa_arg.auxiliary_id = auxiliaryId;
+	pa_arg.is_internal = is_internal;
+
+	(*object_access_hook_str) (OAT_POST_ALTER,
+						   classId, objectName, subId,
+						   (void *) &pa_arg);
+}
+
+/*
+ * RunNamespaceSearchHook
+ *
+ * It is entrypoint of OAT_NAMESPACE_SEARCH event
+ */
+bool
+RunNamespaceSearchHookStr(const char *objectName, bool ereport_on_violation)
+{
+	ObjectAccessNamespaceSearch ns_arg;
+
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	memset(&ns_arg, 0, sizeof(ObjectAccessNamespaceSearch));
+	ns_arg.ereport_on_violation = ereport_on_violation;
+	ns_arg.result = true;
+
+	(*object_access_hook_str) (OAT_NAMESPACE_SEARCH,
+						   NamespaceRelationId, objectName, 0,
+						   (void *) &ns_arg);
+
+	return ns_arg.result;
+}
+
+/*
+ * RunFunctionExecuteHook
+ *
+ * It is entrypoint of OAT_FUNCTION_EXECUTE event
+ */
+void
+RunFunctionExecuteHookStr(const char *objectName)
+{
+	/* caller should check, but just in case... */
+	Assert(object_access_hook_str != NULL);
+
+	(*object_access_hook_str) (OAT_FUNCTION_EXECUTE,
+						   ProcedureRelationId, objectName, 0,
+						   NULL);
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e7f0a380e6..932aefc777 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -43,6 +43,7 @@
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
 #include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
 #include "catalog/pg_authid.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
@@ -8736,6 +8737,18 @@ AlterSystemSetConfigFile(AlterSystemStmt *altersysstmt)
 		replace_auto_config_value(&head, &tail, name, value);
 	}
 
+	/*
+	 * Invoke the post-alter hook for altering this GUC variable.
+	 *
+	 * We do this here rather than at the end, because ALTER SYSTEM is not
+	 * transactional.  If the hook aborts our transaction, it will be cleaner
+	 * to do so before we touch any files.
+	 */
+	InvokeObjectPostAlterHookArgStr(InvalidOid, name,
+									ACL_ALTER_SYSTEM,
+									altersysstmt->setstmt->kind,
+									false);
+
 	/*
 	 * To ensure crash safety, first write the new file data to a temp file,
 	 * then atomically rename it into place.
@@ -8907,6 +8920,10 @@ ExecSetVariableStmt(VariableSetStmt *stmt, bool isTopLevel)
 			ResetAllOptions();
 			break;
 	}
+
+	/* Invoke the post-alter hook for setting this GUC variable. */
+	InvokeObjectPostAlterHookArgStr(InvalidOid, stmt->name,
+									ACL_SET_VALUE, stmt->kind, false);
 }
 
 /*
diff --git a/src/include/catalog/objectaccess.h b/src/include/catalog/objectaccess.h
index 508dfd0a6b..4d54ae2a7d 100644
--- a/src/include/catalog/objectaccess.h
+++ b/src/include/catalog/objectaccess.h
@@ -121,15 +121,23 @@ typedef struct
 	bool		result;
 } ObjectAccessNamespaceSearch;
 
-/* Plugin provides a hook function matching this signature. */
+/* Plugin provides a hook function matching one or both of these signatures. */
 typedef void (*object_access_hook_type) (ObjectAccessType access,
 										 Oid classId,
 										 Oid objectId,
 										 int subId,
 										 void *arg);
 
+typedef void (*object_access_hook_type_str) (ObjectAccessType access,
+										 Oid classId,
+										 const char *objectStr,
+										 int subId,
+										 void *arg);
+
 /* Plugin sets this variable to a suitable hook function. */
 extern PGDLLIMPORT object_access_hook_type object_access_hook;
+extern PGDLLIMPORT object_access_hook_type_str object_access_hook_str;
+
 
 /* Core code uses these functions to call the hook (see macros below). */
 extern void RunObjectPostCreateHook(Oid classId, Oid objectId, int subId,
@@ -142,6 +150,18 @@ extern void RunObjectPostAlterHook(Oid classId, Oid objectId, int subId,
 extern bool RunNamespaceSearchHook(Oid objectId, bool ereport_on_violation);
 extern void RunFunctionExecuteHook(Oid objectId);
 
+/* String versions */
+extern void RunObjectPostCreateHookStr(Oid classId, const char *objectStr, int subId,
+									bool is_internal);
+extern void RunObjectDropHookStr(Oid classId, const char *objectStr, int subId,
+							  int dropflags);
+extern void RunObjectTruncateHookStr(const char *objectStr);
+extern void RunObjectPostAlterHookStr(Oid classId, const char *objectStr, int subId,
+								   Oid auxiliaryId, bool is_internal);
+extern bool RunNamespaceSearchHookStr(const char *objectStr, bool ereport_on_violation);
+extern void RunFunctionExecuteHookStr(const char *objectStr);
+
+
 /*
  * The following macros are wrappers around the functions above; these should
  * normally be used to invoke the hook in lieu of calling the above functions
@@ -194,4 +214,52 @@ extern void RunFunctionExecuteHook(Oid objectId);
 			RunFunctionExecuteHook(objectId);	\
 	} while(0)
 
+
+#define InvokeObjectPostCreateHookStr(classId,objectName,subId)			\
+	InvokeObjectPostCreateHookArgStr((classId),(objectName),(subId),false)
+#define InvokeObjectPostCreateHookArgStr(classId,objectName,subId,is_internal) \
+	do {															\
+		if (object_access_hook_str)										\
+			RunObjectPostCreateHookStr((classId),(objectName),(subId),	\
+									(is_internal));					\
+	} while(0)
+
+#define InvokeObjectDropHookStr(classId,objectName,subId)				\
+	InvokeObjectDropHookArgStr((classId),(objectName),(subId),0)
+#define InvokeObjectDropHookArgStr(classId,objectName,subId,dropflags)	\
+	do {															\
+		if (object_access_hook_str)										\
+			RunObjectDropHookStr((classId),(objectName),(subId),			\
+							  (dropflags));							\
+	} while(0)
+
+#define InvokeObjectTruncateHookStr(objectName)							\
+	do {															\
+		if (object_access_hook_str)										\
+			RunObjectTruncateHookStr(objectName);						\
+	} while(0)
+
+#define InvokeObjectPostAlterHookStr(className,objectName,subId)			\
+	InvokeObjectPostAlterHookArgStr((classId),(objectName),(subId),		\
+								 InvalidOid,false)
+#define InvokeObjectPostAlterHookArgStr(classId,objectName,subId,		\
+									 auxiliaryId,is_internal)		\
+	do {															\
+		if (object_access_hook_str)										\
+			RunObjectPostAlterHookStr((classId),(objectName),(subId),	\
+								   (auxiliaryId),(is_internal));	\
+	} while(0)
+
+#define InvokeNamespaceSearchHookStr(objectName, ereport_on_violation)	\
+	(!object_access_hook_str										\
+	 ? true															\
+	 : RunNamespaceSearchHookStr((objectName), (ereport_on_violation)))
+
+#define InvokeFunctionExecuteHookStr(objectName)		\
+	do {										\
+		if (object_access_hook_str)					\
+			RunFunctionExecuteHookStr(objectName);	\
+	} while(0)
+
+
 #endif							/* OBJECTACCESS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1617702d9d..dc361d62e2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -92,7 +92,9 @@ typedef uint32 AclMode;			/* a bitmask of privilege bits */
 #define ACL_CREATE		(1<<9)	/* for namespaces and databases */
 #define ACL_CREATE_TEMP (1<<10) /* for databases */
 #define ACL_CONNECT		(1<<11) /* for databases */
-#define N_ACL_RIGHTS	12		/* 1 plus the last 1<<x */
+#define ACL_SET_VALUE	(1<<12) /* for configuration parameters */
+#define ACL_ALTER_SYSTEM (1<<13) /* for configuration parameters */
+#define N_ACL_RIGHTS	14		/* 1 plus the last 1<<x */
 #define ACL_NO_RIGHTS	0
 /* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */
 #define ACL_SELECT_FOR_UPDATE	ACL_UPDATE
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 1ce4c5556e..91ce3d8e9c 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -146,9 +146,11 @@ typedef struct ArrayType Acl;
 #define ACL_CREATE_CHR			'C'
 #define ACL_CREATE_TEMP_CHR		'T'
 #define ACL_CONNECT_CHR			'c'
+#define ACL_SET_VALUE_CHR		's'
+#define ACL_ALTER_SYSTEM_CHR	'A'
 
 /* string holding all privilege code chars, in order by bitmask position */
-#define ACL_ALL_RIGHTS_STR	"arwdDxtXUCTc"
+#define ACL_ALL_RIGHTS_STR	"arwdDxtXUCTcsA"
 
 /*
  * Bitmasks defining "all rights" for each supported object type
-- 
2.35.1

