deparse: support GRANT/REVOKE
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 12 Jun 2014 22:34:53 +0000 (18:34 -0400)
committerAndres Freund <andres@anarazel.de>
Fri, 5 Sep 2014 20:11:26 +0000 (22:11 +0200)
src/backend/catalog/aclchk.c
src/backend/commands/event_trigger.c
src/backend/tcop/deparse_utility.c
src/include/commands/event_trigger.h
src/include/tcop/deparse_utility.h
src/include/utils/aclchk.h [new file with mode: 0644]

index d9745cabd244ae5ca42d288fae73f57531f8e676..b2e23120642d371471ea526c57a52926d6e7002f 100644 (file)
@@ -48,6 +48,7 @@
 #include "catalog/pg_ts_config.h"
 #include "catalog/pg_ts_dict.h"
 #include "commands/dbcommands.h"
+#include "commands/event_trigger.h"
 #include "commands/proclang.h"
 #include "commands/tablespace.h"
 #include "foreign/foreign.h"
@@ -56,6 +57,7 @@
 #include "parser/parse_func.h"
 #include "parser/parse_type.h"
 #include "utils/acl.h"
+#include "utils/aclchk.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/tqual.h"
 
 
-/*
- * The information about one Grant/Revoke statement, in internal format: object
- * and grantees names have been turned into Oids, the privilege list is an
- * AclMode bitmask.  If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
- * all_privs is true, 'privileges' will be internally set to the right kind of
- * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
- * InternalGrant struct!)
- *
- * Note: 'all_privs' and 'privileges' represent object-level privileges only.
- * There might also be column-level privilege specifications, which are
- * represented in col_privs (this is a list of untransformed AccessPriv nodes).
- * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
- */
-typedef struct
-{
-   bool        is_grant;
-   GrantObjectType objtype;
-   List       *objects;
-   bool        all_privs;
-   AclMode     privileges;
-   List       *col_privs;
-   List       *grantees;
-   bool        grant_option;
-   DropBehavior behavior;
-} InternalGrant;
-
 /*
  * Internal format used by ALTER DEFAULT PRIVILEGES.
  */
@@ -602,6 +578,14 @@ ExecGrantStmt_oids(InternalGrant *istmt)
            elog(ERROR, "unrecognized GrantStmt.objtype: %d",
                 (int) istmt->objtype);
    }
+
+   /*
+    * Pass the info to event triggers about the just-executed GRANT.  Note
+    * that we prefer to do it after actually executing it, because that gives
+    * the functions a chance to adjust the istmt with privileges actually
+    * granted.
+    */
+   EventTriggerStashGrant(istmt);
 }
 
 /*
index 76c4c1625a657ddfeb0579a46c4540d7477aa42d..33879e1b000c8401a4e2fb76e700162f99cdee74 100644 (file)
@@ -1448,6 +1448,48 @@ EventTriggerComplexCmdEnd(void)
    currentEventTriggerState->curcmd = NULL;
 }
 
+/*
+ * EventTriggerStashGrant
+ *         Save data about a GRANT/REVOKE command being executed
+ *
+ * This function creates a copy of the InternalGrant, as the original might
+ * not have the right lifetime.
+ */
+void
+EventTriggerStashGrant(InternalGrant *istmt)
+{
+   MemoryContext oldcxt;
+   StashedCommand *stashed;
+   InternalGrant  *icopy;
+   ListCell       *cell;
+
+   oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+   /*
+    * copying the node is moderately challenging ... XXX should we consider
+    * changing InternalGrant into a full-fledged node instead?
+    */
+   icopy = palloc(sizeof(InternalGrant));
+   memcpy(icopy, istmt, sizeof(InternalGrant));
+   icopy->objects = list_copy(istmt->objects);
+   icopy->grantees = list_copy(istmt->grantees);
+   icopy->col_privs = NIL;
+   foreach(cell, istmt->col_privs)
+       icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
+
+   stashed = palloc(sizeof(StashedCommand));
+   stashed->type = SCT_Grant;
+   stashed->in_extension = currentEventTriggerState->in_extension;
+
+   stashed->d.grant.istmt = icopy;
+   stashed->parsetree = NULL;
+
+   currentEventTriggerState->stash = lappend(currentEventTriggerState->stash,
+                                             stashed);
+
+   MemoryContextSwitchTo(oldcxt);
+}
+
 Datum
 pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
 {
@@ -1612,6 +1654,29 @@ pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
                /* command */
                values[i++] = CStringGetTextDatum(command);
            }
+           else
+           {
+               Assert(cmd->type == SCT_Grant);
+
+               /* classid */
+               nulls[i++] = true;
+               /* objid */
+               nulls[i++] = true;
+               /* objsubid */
+               nulls[i++] = true;
+               /* command tag */
+               values[i++] = CStringGetTextDatum("GRANT"); /* XXX maybe REVOKE or something else */
+               /* object_type */
+               values[i++] = CStringGetTextDatum("TABLE"); /* XXX maybe something else */
+               /* schema */
+               nulls[i++] = true;
+               /* identity */
+               nulls[i++] = true;
+               /* in_extension */
+               values[i++] = BoolGetDatum(cmd->in_extension);
+               /* command */
+               values[i++] = CStringGetTextDatum(command);
+           }
 
            tuplestore_putvalues(tupstore, tupdesc, values, nulls);
        }
index 44c107184186fab772438b95525aa412205886ab..e47a2feacc135e35f249718dd4d9172d70c945fe 100644 (file)
 #include "catalog/index.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_authid.h"
 #include "catalog/pg_class.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_extension.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_namespace.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_opfamily.h"
@@ -3854,6 +3859,218 @@ deparse_CreateOpFamily(Oid objectId, Node *parsetree)
    return command;
 }
 
+static char *
+deparse_GrantStmt(StashedCommand *cmd)
+{
+   InternalGrant *istmt;
+   ObjTree    *grantStmt;
+   char       *command;
+   char       *fmt;
+   char       *objtype;
+   List       *list;
+   ListCell   *cell;
+   Oid         classId;
+   ObjTree    *tmp;
+
+   istmt = cmd->d.grant.istmt;
+
+   switch (istmt->objtype)
+   {
+       case ACL_OBJECT_COLUMN:
+       case ACL_OBJECT_RELATION:
+           objtype = "TABLE";
+           classId = RelationRelationId;
+           break;
+       case ACL_OBJECT_SEQUENCE:
+           objtype = "SEQUENCE";
+           classId = RelationRelationId;
+           break;
+       case ACL_OBJECT_DOMAIN:
+           objtype = "DOMAIN";
+           classId = TypeRelationId;
+           break;
+       case ACL_OBJECT_FDW:
+           objtype = "FOREIGN DATA WRAPPER";
+           classId = ForeignDataWrapperRelationId;
+           break;
+       case ACL_OBJECT_FOREIGN_SERVER:
+           objtype = "SERVER";
+           classId = ForeignServerRelationId;
+           break;
+       case ACL_OBJECT_FUNCTION:
+           objtype = "FUNCTION";
+           classId = ProcedureRelationId;
+           break;
+       case ACL_OBJECT_LANGUAGE:
+           objtype = "LANGUAGE";
+           classId = LanguageRelationId;
+           break;
+       case ACL_OBJECT_LARGEOBJECT:
+           objtype = "LARGE OBJECT";
+           classId = LargeObjectRelationId;
+           break;
+       case ACL_OBJECT_NAMESPACE:
+           objtype = "SCHEMA";
+           classId = NamespaceRelationId;
+           break;
+       case ACL_OBJECT_TYPE:
+           objtype = "TYPE";
+           classId = TypeRelationId;
+           break;
+       case ACL_OBJECT_DATABASE:
+       case ACL_OBJECT_TABLESPACE:
+           objtype = "";
+           classId = InvalidOid;
+           elog(ERROR, "global objects not supported");
+       default:
+           elog(ERROR, "invalid ACL_OBJECT value %d", istmt->objtype);
+   }
+
+   /* GRANT TO or REVOKE FROM */
+   if (istmt->is_grant)
+       fmt = psprintf("GRANT %%{privileges:, }s ON %s %%{privtarget:, }s "
+                      "TO %%{grantees:, }s %%{grant_option}s",
+                      objtype);
+   else
+       fmt = psprintf("REVOKE %%{grant_option}s %%{privileges:, }s ON %s %%{privtarget:, }s "
+                      "FROM %%{grantees:, }s %%{cascade}s",
+                      objtype);
+
+   grantStmt = new_objtree_VA(fmt, 0);
+
+   /* build list of privileges to grant/revoke */
+   if (istmt->all_privs)
+   {
+       tmp = new_objtree_VA("ALL PRIVILEGES", 0);
+       list = list_make1(new_object_object(NULL, tmp));
+   }
+   else
+   {
+       list = NIL;
+
+       if (istmt->privileges & ACL_INSERT)
+           list = lappend(list, new_string_object(NULL, "INSERT"));
+       if (istmt->privileges & ACL_SELECT)
+           list = lappend(list, new_string_object(NULL, "SELECT"));
+       if (istmt->privileges & ACL_UPDATE)
+           list = lappend(list, new_string_object(NULL, "UPDATE"));
+       if (istmt->privileges & ACL_DELETE)
+           list = lappend(list, new_string_object(NULL, "DELETE"));
+       if (istmt->privileges & ACL_TRUNCATE)
+           list = lappend(list, new_string_object(NULL, "TRUNCATE"));
+       if (istmt->privileges & ACL_REFERENCES)
+           list = lappend(list, new_string_object(NULL, "REFERENCES"));
+       if (istmt->privileges & ACL_TRIGGER)
+           list = lappend(list, new_string_object(NULL, "TRIGGER"));
+       if (istmt->privileges & ACL_EXECUTE)
+           list = lappend(list, new_string_object(NULL, "EXECUTE"));
+       if (istmt->privileges & ACL_USAGE)
+           list = lappend(list, new_string_object(NULL, "USAGE"));
+       if (istmt->privileges & ACL_CREATE)
+           list = lappend(list, new_string_object(NULL, "CREATE"));
+       if (istmt->privileges & ACL_CREATE_TEMP)
+           list = lappend(list, new_string_object(NULL, "TEMPORARY"));
+       if (istmt->privileges & ACL_CONNECT)
+           list = lappend(list, new_string_object(NULL, "CONNECT"));
+
+       if (istmt->col_privs != NIL)
+       {
+           ListCell   *ocell;
+
+           foreach(ocell, istmt->col_privs)
+           {
+               AccessPriv *priv = lfirst(ocell);
+               List   *cols = NIL;
+
+               tmp = new_objtree_VA("%{priv}s (%{cols:, }I)", 0);
+               foreach(cell, priv->cols)
+               {
+                   Value *colname = lfirst(cell);
+
+                   cols = lappend(cols,
+                                  new_string_object(NULL,
+                                                    strVal(colname)));
+               }
+               append_array_object(tmp, "cols", cols);
+               if (priv->priv_name == NULL)
+                   append_string_object(tmp, "priv", "ALL PRIVILEGES");
+               else
+                   append_string_object(tmp, "priv", priv->priv_name);
+
+               list = lappend(list, new_object_object(NULL, tmp));
+           }
+       }
+   }
+   append_array_object(grantStmt, "privileges", list);
+
+   /* target objects.  We use object identities here */
+   list = NIL;
+   foreach(cell, istmt->objects)
+   {
+       Oid     objid = lfirst_oid(cell);
+       ObjectAddress addr;
+
+       addr.classId = classId;
+       addr.objectId = objid;
+       addr.objectSubId = 0;
+
+       tmp = new_objtree_VA("%{identity}s", 0);
+       append_string_object(tmp, "identity",
+                            getObjectIdentity(&addr));
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+   append_array_object(grantStmt, "privtarget", list);
+
+   /* list of grantees */
+   list = NIL;
+   foreach(cell, istmt->grantees)
+   {
+       Oid     grantee = lfirst_oid(cell);
+
+       if (grantee == ACL_ID_PUBLIC)
+           tmp = new_objtree_VA("PUBLIC", 0);
+       else
+       {
+           HeapTuple   roltup;
+           char       *rolname;
+
+           roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(grantee));
+           if (!HeapTupleIsValid(roltup))
+               ereport(ERROR,
+                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                        errmsg("role with OID %u does not exist", grantee)));
+
+           tmp = new_objtree_VA("%{name}I", 0);
+           rolname = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname);
+           append_string_object(tmp, "name", pstrdup(rolname));
+           ReleaseSysCache(roltup);
+       }
+       list = lappend(list, new_object_object(NULL, tmp));
+   }
+   append_array_object(grantStmt, "grantees", list);
+
+   /* the wording of the grant option is variable ... */
+   if (istmt->is_grant)
+       append_string_object(grantStmt, "grant_option",
+                            istmt->grant_option ?  "WITH GRANT OPTION" : "");
+   else
+       append_string_object(grantStmt, "grant_option",
+                            istmt->grant_option ?  "GRANT OPTION FOR" : "");
+
+   if (!istmt->is_grant)
+   {
+       if (istmt->behavior == DROP_CASCADE)
+           append_string_object(grantStmt, "cascade", "CASCADE");
+       else
+           append_string_object(grantStmt, "cascade", "");
+   }
+
+   command = jsonize_objtree(grantStmt);
+   free_objtree(grantStmt);
+
+   return command;
+}
+
 static char *
 deparse_AlterTableStmt(StashedCommand *cmd)
 {
@@ -4378,6 +4595,10 @@ deparse_parsenode_cmd(StashedCommand *cmd)
            command = deparse_AlterOwnerStmt(objectId, parsetree);
            break;
 
+       case T_GrantStmt:
+           elog(ERROR, "unexpected node type T_GrantStmt");
+           break;
+
        default:
            command = NULL;
            elog(LOG, "unrecognized node type: %d",
@@ -4433,6 +4654,9 @@ deparse_utility_command(StashedCommand *cmd)
        case SCT_AlterTable:
            command = deparse_parsenode_cmd(cmd);
            break;
+       case SCT_Grant:
+           command = deparse_GrantStmt(cmd);
+           break;
        default:
            elog(ERROR, "unexpected deparse node type %d", cmd->type);
    }
index 3e84cf79cff51dda90fec7002fc0aa840405a65b..814124444f7021115271a4ef23196f7d163c7160 100644 (file)
@@ -17,6 +17,7 @@
 #include "catalog/objectaddress.h"
 #include "catalog/pg_event_trigger.h"
 #include "nodes/parsenodes.h"
+#include "utils/aclchk.h"
 
 typedef struct EventTriggerData
 {
@@ -56,6 +57,7 @@ extern void EventTriggerStashExtensionStart(void);
 extern void EventTriggerStashExtensionStop(void);
 extern void EventTriggerStashCommand(Oid objectId, ObjectType objtype,
                         Node *parsetree);
+extern void EventTriggerStashGrant(InternalGrant *istmt);
 extern void EventTriggerComplexCmdStart(Node *parsetree, ObjectType objtype);
 extern void EventTriggerComplexCmdSetOid(Oid objectId);
 extern void EventTriggerRecordSubcmd(Node *subcmd, Oid relid,
index 37b38f00a898fb73dd473056b1c2fc1dc899e366..61ad728a32b49f3d6b37b0d7f7379a78ce541bc7 100644 (file)
@@ -14,6 +14,8 @@
 
 #include "access/attnum.h"
 #include "nodes/nodes.h"
+#include "utils/aclchk.h"
+
 
 /*
  * Support for keeping track of a command to deparse.
@@ -26,7 +28,8 @@
 typedef enum StashedCommandType
 {
    SCT_Basic,
-   SCT_AlterTable
+   SCT_AlterTable,
+   SCT_Grant
 } StashedCommandType;
 
 /*
@@ -60,6 +63,11 @@ typedef struct StashedCommand
            ObjectType objtype;
            List   *subcmds;
        } alterTable;
+
+       struct GrantCommand
+       {
+           InternalGrant *istmt;
+       } grant;
    } d;
 } StashedCommand;
 
diff --git a/src/include/utils/aclchk.h b/src/include/utils/aclchk.h
new file mode 100644 (file)
index 0000000..1ca7095
--- /dev/null
@@ -0,0 +1,45 @@
+/*-------------------------------------------------------------------------
+ *
+ * aclchk.h
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/aclchk.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef ACLCHK_H
+#define ACLCHK_H
+
+#include "nodes/parsenodes.h"
+#include "nodes/pg_list.h"
+
+/*
+ * The information about one Grant/Revoke statement, in internal format: object
+ * and grantees names have been turned into Oids, the privilege list is an
+ * AclMode bitmask.  If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
+ * all_privs is true, 'privileges' will be internally set to the right kind of
+ * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
+ * InternalGrant struct!)
+ *
+ * Note: 'all_privs' and 'privileges' represent object-level privileges only.
+ * There might also be column-level privilege specifications, which are
+ * represented in col_privs (this is a list of untransformed AccessPriv nodes).
+ * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
+ */
+typedef struct
+{
+   bool        is_grant;
+   GrantObjectType objtype;
+   List       *objects;
+   bool        all_privs;
+   AclMode     privileges;
+   List       *col_privs;
+   List       *grantees;
+   bool        grant_option;
+   DropBehavior behavior;
+} InternalGrant;
+
+
+#endif /* ACLCHK_H */