diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index f645c1252d625d680c409e86bff09f40f9e2fad3..96cbf1f12ad38e0f3aa0f2c933f634e887c3a9b5 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.36 2008/12/19 16:25:16 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/information_schema.sgml,v 1.37 2009/01/20 09:10:20 petere Exp $ --> <chapter id="information-schema"> <title>The Information Schema</title> @@ -5081,7 +5081,12 @@ ORDER BY c.ordinal_position; <row> <entry><literal>option_value</literal></entry> <entry><type>character_data</type></entry> - <entry>Value of the option</entry> + <entry>Value of the option. This column will show as null + unless the current user is the user being mapped, or the mapping + is for <literal>PUBLIC</literal> and the current user is the + server owner, or the current user is a superuser. The intent is + to protect password information stored as user mapping + option.</entry> </row> </tbody> </tgroup> diff --git a/doc/src/sgml/ref/alter_user_mapping.sgml b/doc/src/sgml/ref/alter_user_mapping.sgml index 38bff39ece7204dbbab11761e8acbb1a218630ea..fef968a32e18759e3abdca559b0445fbe243c694 100644 --- a/doc/src/sgml/ref/alter_user_mapping.sgml +++ b/doc/src/sgml/ref/alter_user_mapping.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/alter_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_user_mapping.sgml,v 1.2 2009/01/20 09:10:20 petere Exp $ PostgreSQL documentation --> @@ -31,10 +31,15 @@ ALTER USER MAPPING FOR { <replaceable class="parameter">username</replaceable> | <para> <command>ALTER USER MAPPING</command> changes the definition of a - user mapping. Only the owner of the server can change the user - mappings of that server. + user mapping. </para> + <para> + The owner of a foreign server can alter user mappings for that + server for any user. Also, a user can alter a user mapping for + his own user name if <literal>USAGE</> privilege on the server has + been granted to the user. + </para> </refsect1> <refsect1> diff --git a/doc/src/sgml/ref/create_user_mapping.sgml b/doc/src/sgml/ref/create_user_mapping.sgml index b05898174926ca9b61e665a36345b11d43289068..6857b3eb7e9061a67824bb98efe7e5da295e595c 100644 --- a/doc/src/sgml/ref/create_user_mapping.sgml +++ b/doc/src/sgml/ref/create_user_mapping.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_user_mapping.sgml,v 1.2 2009/01/17 04:24:41 neilc Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_user_mapping.sgml,v 1.3 2009/01/20 09:10:20 petere Exp $ PostgreSQL documentation --> @@ -31,10 +31,15 @@ CREATE USER MAPPING FOR { <replaceable class="parameter">username</replaceable> <para> <command>CREATE USER MAPPING</command> defines a mapping of a user - to a foreign server. You must be the owner of the server to define - user mappings for it. + to a foreign server. </para> + <para> + The owner of a foreign server can create user mappings for that + server for any user. Also, a user can create a user mapping for + his own user name if <literal>USAGE</> privilege on the server has + been granted to the user. + </para> </refsect1> <refsect1> diff --git a/doc/src/sgml/ref/drop_user_mapping.sgml b/doc/src/sgml/ref/drop_user_mapping.sgml index c22dedb661ad5227a6cf3ac0fce85173052cdb2b..82d5fa5dcb1cecadd5b87d0936e832fdcb419bf3 100644 --- a/doc/src/sgml/ref/drop_user_mapping.sgml +++ b/doc/src/sgml/ref/drop_user_mapping.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/drop_user_mapping.sgml,v 1.1 2008/12/19 16:25:16 petere Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_user_mapping.sgml,v 1.2 2009/01/20 09:10:20 petere Exp $ PostgreSQL documentation --> @@ -29,8 +29,14 @@ DROP USER MAPPING [ IF EXISTS ] FOR { <replaceable class="parameter">username</r <para> <command>DROP USER MAPPING</command> removes an existing user - mapping from foreign server. To execute this command, the current - user must be the owner of the server containing the mapping. + mapping from foreign server. + </para> + + <para> + The owner of a foreign server can drop user mappings for that server + for any user. Also, a user can drop a user mapping for his own + user name if <literal>USAGE</> privilege on the server has been + granted to the user. </para> </refsect1> diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 5bb46eedf8a275177e33561c5bc3d7081cb13697..470a454f69099c8e35fba83b249762b076a8151a 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -4,7 +4,7 @@ * * Copyright (c) 2003-2009, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.49 2009/01/14 21:12:09 petere Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.50 2009/01/20 09:10:20 petere Exp $ */ /* @@ -2465,12 +2465,12 @@ CREATE VIEW _pg_foreign_servers AS s.srvoptions, CAST(current_database() AS sql_identifier) AS foreign_server_catalog, CAST(srvname AS sql_identifier) AS foreign_server_name, - w.foreign_data_wrapper_catalog, - w.foreign_data_wrapper_name, + CAST(current_database() AS sql_identifier) AS foreign_data_wrapper_catalog, + CAST(w.fdwname AS sql_identifier) AS foreign_data_wrapper_name, CAST(srvtype AS character_data) AS foreign_server_type, CAST(srvversion AS character_data) AS foreign_server_version, CAST(u.rolname AS sql_identifier) AS authorization_identifier - FROM pg_foreign_server s, _pg_foreign_data_wrappers w, pg_authid u + FROM pg_foreign_server s, pg_foreign_data_wrapper w, pg_authid u WHERE w.oid = s.srvfdw AND u.oid = s.srvowner AND (pg_has_role(s.srvowner, 'USAGE') @@ -2512,9 +2512,11 @@ GRANT SELECT ON foreign_servers TO PUBLIC; CREATE VIEW _pg_user_mappings AS SELECT um.oid, um.umoptions, + um.umuser, CAST(COALESCE(u.rolname,'PUBLIC') AS sql_identifier ) AS authorization_identifier, s.foreign_server_catalog, - s.foreign_server_name + s.foreign_server_name, + s.authorization_identifier AS srvowner FROM pg_user_mapping um LEFT JOIN pg_authid u ON (u.oid = um.umuser), _pg_foreign_servers s WHERE s.oid = um.umserver; @@ -2529,7 +2531,10 @@ CREATE VIEW user_mapping_options AS foreign_server_catalog, foreign_server_name, CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name, - CAST((pg_options_to_table(um.umoptions)).option_value AS character_data) AS option_value + CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user) + OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE')) + OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value + ELSE NULL END AS character_data) AS option_value FROM _pg_user_mappings um; GRANT SELECT ON user_mapping_options TO PUBLIC; diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 17336c35f38301949cac6dd7d3b32e069d0952f9..0967001aa3f61fcb73d9d86d3d5cec7e26d0f932 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.4 2009/01/01 17:23:38 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/foreigncmds.c,v 1.5 2009/01/20 09:10:20 petere Exp $ * *------------------------------------------------------------------------- */ @@ -828,6 +828,33 @@ RemoveForeignServerById(Oid srvId) } +/* + * Common routine to check permission for user-mapping-related DDL + * commands. We allow server owners to operate on any mapping, and + * users to operate on their own mapping. + */ +static void +user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername) +{ + Oid curuserid = GetUserId(); + + if (!pg_foreign_server_ownercheck(serverid, curuserid)) + { + if (umuserid == curuserid) + { + AclResult aclresult; + + aclresult = pg_foreign_server_aclcheck(serverid, curuserid, ACL_USAGE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FOREIGN_SERVER, servername); + } + else + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + servername); + } +} + + /* * Create user mapping */ @@ -841,24 +868,17 @@ CreateUserMapping(CreateUserMappingStmt *stmt) HeapTuple tuple; Oid useId; Oid umId; - Oid ownerId; ObjectAddress myself; ObjectAddress referenced; ForeignServer *srv; ForeignDataWrapper *fdw; - ownerId = GetUserId(); - useId = GetUserOidFromMapping(stmt->username, false); - /* - * Check that the server exists and that the we own it. - */ + /* Check that the server exists. */ srv = GetForeignServerByName(stmt->servername, false); - if (!pg_foreign_server_ownercheck(srv->serverid, ownerId)) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); /* * Check that the user mapping is unique within server. @@ -951,12 +971,7 @@ AlterUserMapping(AlterUserMappingStmt *stmt) errmsg("user mapping \"%s\" does not exist for the server", MappingUserName(useId)))); - /* - * Must be owner of the server to alter user mapping. - */ - if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); + user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); tp = SearchSysCacheCopy(USERMAPPINGOID, ObjectIdGetDatum(umId), @@ -1071,14 +1086,7 @@ RemoveUserMapping(DropUserMappingStmt *stmt) return; } - /* - * Only allow DROP if we own the server. - */ - if (!pg_foreign_server_ownercheck(srv->serverid, GetUserId())) - { - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - srv->servername); - } + user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); /* * Do the deletion diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index 35dcae672b441134233a60239f36d2779e43ceb1..62d060b209aa6c28888d55995e2e20ed6199da1b 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -545,7 +545,7 @@ SET ROLE regress_test_role; CREATE USER MAPPING FOR current_user SERVER s5; CREATE USER MAPPING FOR current_user SERVER s6 OPTIONS (username 'test'); CREATE USER MAPPING FOR current_user SERVER s7; -- ERROR -ERROR: must be owner of foreign server s7 +ERROR: permission denied for foreign server s7 CREATE USER MAPPING FOR public SERVER s8; -- ERROR ERROR: must be owner of foreign server s8 RESET ROLE; @@ -736,6 +736,13 @@ SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREI (2 rows) DROP USER MAPPING FOR current_user SERVER st1; +SET ROLE regress_test_role2; +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; + authorization_identifier | foreign_server_catalog | foreign_server_name | option_name | option_value +--------------------------+------------------------+---------------------+-------------+-------------- + regress_test_role | regression | s6 | username | +(1 row) + RESET ROLE; -- has_foreign_data_wrapper_privilege SELECT has_foreign_data_wrapper_privilege('regress_test_role', @@ -932,8 +939,7 @@ ALTER SERVER s9 VERSION '1.2'; -- ERROR ERROR: must be owner of foreign server s9 GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; -- WARNING WARNING: no privileges were granted for "s9" -CREATE USER MAPPING FOR current_user SERVER s9; -- ERROR -ERROR: must be owner of foreign server s9 +CREATE USER MAPPING FOR current_user SERVER s9; DROP SERVER s9 CASCADE; -- ERROR ERROR: must be owner of foreign server s9 RESET ROLE; @@ -953,11 +959,12 @@ NOTICE: drop cascades to user mapping for public DROP SERVER st2; DROP USER MAPPING FOR regress_test_role SERVER s6; DROP FOREIGN DATA WRAPPER foo CASCADE; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to server s4 drop cascades to user mapping for foreign_data_user drop cascades to server s6 drop cascades to server s9 +drop cascades to user mapping for unprivileged_role DROP SERVER s8 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to user mapping for foreign_data_user diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql index 1424fc68f08c6d490e1d2ca40dd749dfd7be79b2..c52fe912c1d128b65ce93fbc871f59cbae567eb3 100644 --- a/src/test/regress/sql/foreign_data.sql +++ b/src/test/regress/sql/foreign_data.sql @@ -273,6 +273,8 @@ SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; SELECT * FROM information_schema.usage_privileges WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; SELECT * FROM information_schema.role_usage_grants WHERE object_type LIKE 'FOREIGN%' ORDER BY 1, 2, 3, 4, 5; DROP USER MAPPING FOR current_user SERVER st1; +SET ROLE regress_test_role2; +SELECT * FROM information_schema.user_mapping_options ORDER BY 1, 2, 3, 4; RESET ROLE; @@ -365,7 +367,7 @@ GRANT USAGE ON FOREIGN SERVER s9 TO unprivileged_role; SET ROLE unprivileged_role; ALTER SERVER s9 VERSION '1.2'; -- ERROR GRANT USAGE ON FOREIGN SERVER s9 TO regress_test_role; -- WARNING -CREATE USER MAPPING FOR current_user SERVER s9; -- ERROR +CREATE USER MAPPING FOR current_user SERVER s9; DROP SERVER s9 CASCADE; -- ERROR RESET ROLE;