From 32a8bc339f47788293228a0f61f4f244ee969a51 Mon Sep 17 00:00:00 2001 From: Shigeru Hanada Date: Mon, 4 Apr 2011 19:07:42 +0900 Subject: [PATCH 1/5] Implement COMMENT ON USER MAPPING. Privilege to COMMENT is similar to ALTER/DROP USER MAPPING. Needs updates for regression tests and documents. --- src/backend/catalog/aclchk.c | 43 +++++++++++++++++++++++++++++++++++ src/backend/catalog/objectaddress.c | 31 +++++++++++++++++++++++++ src/backend/foreign/foreign.c | 36 +++++++++++++++++++++++++++++ src/backend/parser/gram.y | 8 ++++++ src/include/foreign/foreign.h | 2 + src/include/nodes/parsenodes.h | 1 + src/include/utils/acl.h | 1 + 7 files changed, 122 insertions(+), 0 deletions(-) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index aa3d59d..bbc6362 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -44,6 +44,7 @@ #include "catalog/pg_type.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" +#include "catalog/pg_user_mapping.h" #include "commands/dbcommands.h" #include "commands/proclang.h" #include "commands/tablespace.h" @@ -4643,6 +4644,48 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid) } /* + * Ownership check for a user mapping (specified by OID). + * + * User mappings don't have owner, so we treat some users as the owner: + * (1) owner of the foreign server + * (2) user whose name matches the user name of the mapping exactly, and has + * USAGE privilege on the server. + */ +bool +pg_user_mapping_ownercheck(Oid um_oid, Oid roleid) +{ + HeapTuple tuple; + Oid userId; + Oid serverId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(um_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("user mapping with OID %u does not exist", + um_oid))); + + userId = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser; + serverId = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver; + + ReleaseSysCache(tuple); + + if (userId == roleid) + { + AclResult aclresult; + + aclresult = pg_foreign_server_aclcheck(serverId, roleid, ACL_USAGE); + return (aclresult == ACLCHECK_OK); + } + else + return pg_foreign_server_ownercheck(serverId, roleid); +} + +/* * Ownership check for a database (specified by OID). */ bool diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 0d21d31..c322fd8 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -48,6 +48,7 @@ #include "catalog/pg_ts_parser.h" #include "catalog/pg_ts_template.h" #include "catalog/pg_type.h" +#include "catalog/pg_user_mapping.h" #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/extension.h" @@ -232,6 +233,22 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, address.objectId = get_ts_config_oid(objname, false); address.objectSubId = 0; break; + case OBJECT_USER_MAPPING: + { + char *username; + char *servername; + + Assert(list_length(objname) == 2); + + username = strVal(linitial(objname)); + servername = strVal(lsecond(objname)); + address.classId = UserMappingRelationId; + address.objectId = get_user_mapping_oid(username, + servername, + false); + address.objectSubId = 0; + } + break; default: elog(ERROR, "unrecognized objtype: %d", (int) objtype); /* placate compiler, in case it thinks elog might return */ @@ -682,6 +699,9 @@ object_exists(ObjectAddress address) case ForeignServerRelationId: cache = FOREIGNSERVEROID; break; + case UserMappingRelationId: + cache = USERMAPPINGOID; + break; case TSParserRelationId: cache = TSPARSEROID; break; @@ -795,6 +815,17 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, NameListToString(objname)); break; + case OBJECT_USER_MAPPING: + if (!pg_user_mapping_ownercheck(address.objectId, roleid)) + { + char *username = strVal(linitial(objname)); + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of user mapping FOR %s SERVER %s", + username ? username : "public", + strVal(lsecond(objname))))); + } + break; case OBJECT_LANGUAGE: if (!pg_language_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index cda90a6..a84358e 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -538,3 +538,39 @@ get_foreign_server_oid(const char *servername, bool missing_ok) errmsg("server \"%s\" does not exist", servername))); return oid; } + +/* + * get_user_mapping_oid - given a USER name and SERVER name, look up the OID + * + * If missing_ok is false, throw an error if name not found. If true, just + * return InvalidOid. + */ +Oid +get_user_mapping_oid(const char *username, const char *servername, + bool missing_ok) +{ + Oid useroid; + Oid serveroid; + Oid oid; + + /* Determine umuser of the mapping */ + if (username == NULL) + useroid = 0; + else if (strcmp(username, "current_user") == 0) + useroid = GetUserId(); + else + useroid = get_role_oid(username, false); + + /* Determine umserver of the mapping */ + serveroid = get_foreign_server_oid(servername, false); + + oid = GetSysCacheOid2(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(useroid), + ObjectIdGetDatum(serveroid)); + if (!OidIsValid(oid) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("user mapping \"%s\" for the server does not exist", + MappingUserName(useroid)))); + return oid; +} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a22ab66..c234259 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -4954,6 +4954,14 @@ CommentStmt: n->comment = $8; $$ = (Node *) n; } + | COMMENT ON USER MAPPING FOR auth_ident SERVER name IS comment_text + { + CommentStmt *n = makeNode(CommentStmt); + n->objtype = OBJECT_USER_MAPPING; + n->objname = list_make2(makeString($6), makeString($8)); + n->comment = $10; + $$ = (Node *) n; + } ; comment_type: diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index 2fda9e3..f94d940 100644 --- a/src/include/foreign/foreign.h +++ b/src/include/foreign/foreign.h @@ -78,5 +78,7 @@ extern ForeignTable *GetForeignTable(Oid relid); extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok); extern Oid get_foreign_server_oid(const char *servername, bool missing_ok); +extern Oid get_user_mapping_oid(const char *username, const char *servername, + bool missing_ok); #endif /* FOREIGN_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index d9eac76..c464c49 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1133,6 +1133,7 @@ typedef enum ObjectType OBJECT_TSPARSER, OBJECT_TSTEMPLATE, OBJECT_TYPE, + OBJECT_USER_MAPPING, OBJECT_VIEW } ObjectType; diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index b28b764..a74bad5 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -317,6 +317,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid); extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid); extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); +extern bool pg_user_mapping_ownercheck(Oid um_oid, Oid roleid); extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); -- 1.7.3