>From 2b3b3ea0d87bb546d2b3e2c18f311873b7eaa693 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Thu, 12 Jun 2014 18:34:53 -0400 Subject: [PATCH 21/37] deparse: Support GRANT/REVOKE --- src/backend/catalog/aclchk.c | 37 ++----- src/backend/commands/event_trigger.c | 106 +++++++++++++++++++ src/backend/tcop/deparse_utility.c | 193 +++++++++++++++++++++++++++++++++++ src/include/commands/event_trigger.h | 3 + src/include/tcop/deparse_utility.h | 12 ++- src/include/utils/aclchk_internal.h | 45 ++++++++ 6 files changed, 369 insertions(+), 27 deletions(-) create mode 100644 src/include/utils/aclchk_internal.h diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 8e75c27..6eefb9c 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -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_internal.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -65,32 +67,6 @@ /* - * 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. */ typedef struct @@ -605,6 +581,15 @@ 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. + */ + if (EventTriggerSupportsGrantObjectType(istmt->objtype)) + EventTriggerStashGrant(istmt); } /* diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index b3b8627..3c57951 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -1773,6 +1773,88 @@ EventTriggerAlterTableEnd(void) currentEventTriggerState->curcmd = NULL; } +static const char * +stringify_grantobjtype(GrantObjectType objtype) +{ + switch (objtype) + { + case ACL_OBJECT_COLUMN: + return "COLUMN"; + case ACL_OBJECT_RELATION: + return "TABLE"; + case ACL_OBJECT_SEQUENCE: + return "SEQUENCE"; + case ACL_OBJECT_DATABASE: + return "DATABASE"; + case ACL_OBJECT_DOMAIN: + return "DOMAIN"; + case ACL_OBJECT_FDW: + return "FOREIGN DATA WRAPPER"; + case ACL_OBJECT_FOREIGN_SERVER: + return "FOREIGN SERVER"; + case ACL_OBJECT_FUNCTION: + return "FUNCTION"; + case ACL_OBJECT_LANGUAGE: + return "LANGUAGE"; + case ACL_OBJECT_LARGEOBJECT: + return "LARGE OBJECT"; + case ACL_OBJECT_NAMESPACE: + return "SCHEMA"; + case ACL_OBJECT_TABLESPACE: + return "TABLESPACE"; + case ACL_OBJECT_TYPE: + return "TYPE"; + default: + elog(ERROR, "unrecognized type %d", objtype); + return "???"; /* keep compiler quiet */ + } +} + +/* + * 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; + + if (currentEventTriggerState->commandCollectionInhibited) + return; + + oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt); + + /* + * copying the node is moderately challenging ... 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 = creating_extension; + stashed->d.grant.type = stringify_grantobjtype(istmt->objtype); + 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) { @@ -1931,6 +2013,30 @@ 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(cmd->d.grant.istmt->is_grant ? + "GRANT" : "REVOKE"); + /* object_type */ + values[i++] = CStringGetTextDatum(cmd->d.grant.type); + /* 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); } diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c index 46d571a..b7659ba 100644 --- a/src/backend/tcop/deparse_utility.c +++ b/src/backend/tcop/deparse_utility.c @@ -4552,6 +4552,196 @@ deparse_CreateOpFamily(Oid objectId, Node *parsetree) } static ObjTree * +deparse_GrantStmt(StashedCommand *cmd) +{ + InternalGrant *istmt; + ObjTree *grantStmt; + 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 = "FOREIGN 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:, }R %%{grant_option}s", + objtype); + else + fmt = psprintf("REVOKE %%{grant_option}s %%{privileges:, }s ON %s %%{privtarget:, }s " + "FROM %%{grantees:, }R %%{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(tmp)); + } + else + { + list = NIL; + + if (istmt->privileges & ACL_INSERT) + list = lappend(list, new_string_object("INSERT")); + if (istmt->privileges & ACL_SELECT) + list = lappend(list, new_string_object("SELECT")); + if (istmt->privileges & ACL_UPDATE) + list = lappend(list, new_string_object("UPDATE")); + if (istmt->privileges & ACL_DELETE) + list = lappend(list, new_string_object("DELETE")); + if (istmt->privileges & ACL_TRUNCATE) + list = lappend(list, new_string_object("TRUNCATE")); + if (istmt->privileges & ACL_REFERENCES) + list = lappend(list, new_string_object("REFERENCES")); + if (istmt->privileges & ACL_TRIGGER) + list = lappend(list, new_string_object("TRIGGER")); + if (istmt->privileges & ACL_EXECUTE) + list = lappend(list, new_string_object("EXECUTE")); + if (istmt->privileges & ACL_USAGE) + list = lappend(list, new_string_object("USAGE")); + if (istmt->privileges & ACL_CREATE) + list = lappend(list, new_string_object("CREATE")); + if (istmt->privileges & ACL_CREATE_TEMP) + list = lappend(list, new_string_object("TEMPORARY")); + if (istmt->privileges & ACL_CONNECT) + list = lappend(list, new_string_object("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(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(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(tmp)); + } + append_array_object(grantStmt, "privtarget", list); + + /* list of grantees */ + list = NIL; + foreach(cell, istmt->grantees) + { + Oid grantee = lfirst_oid(cell); + + tmp = new_objtree_for_role_id(grantee); + list = lappend(list, new_object_object(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", ""); + } + + return grantStmt; +} + +static ObjTree * deparse_AlterTableStmt(StashedCommand *cmd) { ObjTree *alterTableStmt; @@ -5411,6 +5601,9 @@ deparse_utility_command(StashedCommand *cmd) case SCT_AlterTable: tree = deparse_AlterTableStmt(cmd); break; + case SCT_Grant: + tree = deparse_GrantStmt(cmd); + break; default: elog(ERROR, "unexpected deparse node type %d", cmd->type); } diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index 7a449cc..932630f 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -17,6 +17,7 @@ #include "catalog/objectaddress.h" #include "catalog/pg_event_trigger.h" #include "nodes/parsenodes.h" +#include "utils/aclchk_internal.h" typedef struct EventTriggerData { @@ -72,4 +73,6 @@ extern void EventTriggerAlterTableStashSubcmd(Node *subcmd, Oid relid, ObjectAddress address); extern void EventTriggerAlterTableEnd(void); +extern void EventTriggerStashGrant(InternalGrant *istmt); + #endif /* EVENT_TRIGGER_H */ diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h index 3b68290..7f70ede 100644 --- a/src/include/tcop/deparse_utility.h +++ b/src/include/tcop/deparse_utility.h @@ -14,6 +14,8 @@ #include "access/attnum.h" #include "nodes/nodes.h" +#include "utils/aclchk_internal.h" + /* * Support for keeping track of a command to deparse. @@ -26,7 +28,8 @@ typedef enum StashedCommandType { SCT_Simple, - SCT_AlterTable + SCT_AlterTable, + SCT_Grant } StashedCommandType; /* @@ -61,6 +64,13 @@ typedef struct StashedCommand Oid classId; List *subcmds; } alterTable; + + /* GRANT / REVOKE */ + struct + { + InternalGrant *istmt; + const char *type; + } grant; } d; } StashedCommand; diff --git a/src/include/utils/aclchk_internal.h b/src/include/utils/aclchk_internal.h new file mode 100644 index 0000000..bac2728 --- /dev/null +++ b/src/include/utils/aclchk_internal.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * aclchk_internal.h + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/utils/aclchk_internal.h + * + *------------------------------------------------------------------------- + */ +#ifndef ACLCHK_INTERNAL_H +#define ACLCHK_INTERNAL_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_INTERNAL_H */ -- 2.1.4