>From ee077dcdb7fba2f3b67c45ad5cf8581a8a0d0a9a Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Thu, 12 Jun 2014 18:34:53 -0400 Subject: [PATCH 24/26] deparse: Support GRANT/REVOKE --- src/backend/catalog/aclchk.c | 37 ++---- src/backend/commands/event_trigger.c | 65 +++++++++++ src/backend/tcop/deparse_utility.c | 215 +++++++++++++++++++++++++++++++++++ src/include/commands/event_trigger.h | 2 + src/include/tcop/deparse_utility.h | 10 +- src/include/utils/aclchk.h | 45 ++++++++ 6 files changed, 347 insertions(+), 27 deletions(-) create mode 100644 src/include/utils/aclchk.h diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index d30612c..f9c068f 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.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 @@ -602,6 +578,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 b66301f..62a3332 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -1469,6 +1469,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 = creating_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) { @@ -1637,6 +1679,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); } diff --git a/src/backend/tcop/deparse_utility.c b/src/backend/tcop/deparse_utility.c index c91fc9b..051a4e8 100644 --- a/src/backend/tcop/deparse_utility.c +++ b/src/backend/tcop/deparse_utility.c @@ -32,14 +32,19 @@ #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" @@ -3738,6 +3743,213 @@ 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 = "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(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); + + 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(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; @@ -4409,6 +4621,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 2a23edd..9573459 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.h" typedef struct EventTriggerData { @@ -55,6 +56,7 @@ extern void EventTriggerSQLDropAddObject(ObjectAddress *object); extern void EventTriggerStashCommand(Oid objectId, uint32 objectSubId, 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, diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h index 910c716..6783de7 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.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; /* @@ -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 index 0000000..1ca7095 --- /dev/null +++ b/src/include/utils/aclchk.h @@ -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 */ -- 1.9.1