Fix crash when user mapping has no user name

When a user mapping is created without a user name in the options a
crash will occur when querying a distributed hypertable.

This change fixes the crash by using the "current user" when no user
option exists (i.e., no user mapping is done). This makes it possible
to create user mappings that only provide the password option.
This commit is contained in:
Erik Nordström 2020-12-17 00:37:09 +01:00 committed by Erik Nordström
parent 0f48f1b29d
commit 15e1edb76f
3 changed files with 43 additions and 10 deletions

View File

@ -455,7 +455,6 @@ extract_connection_options(List *defelems, const char **keywords, const char **v
} }
} }
Assert(*user != NULL);
return option_pos; return option_pos;
} }
@ -992,6 +991,10 @@ remote_connection_set_peer_dist_id(TSConnection *conn)
/* sslmode, sslrootcert, sslcert, sslkey */ /* sslmode, sslrootcert, sslcert, sslkey */
#define REMOTE_CONNECTION_SSL_OPTIONS_N 4 #define REMOTE_CONNECTION_SSL_OPTIONS_N 4
#define REMOTE_CONNECTION_OPTIONS_TOTAL_N \
(REMOTE_CONNECTION_SESSION_OPTIONS_N + REMOTE_CONNECTION_PASSWORD_OPTIONS_N + \
REMOTE_CONNECTION_SSL_OPTIONS_N)
/* default password file basename */ /* default password file basename */
#define DEFAULT_PASSFILE_NAME "passfile" #define DEFAULT_PASSFILE_NAME "passfile"
@ -1154,13 +1157,15 @@ remote_connection_open_with_options_nothrow(const char *node_name, List *connect
* for fallback_application_name, client_encoding, end marker. * for fallback_application_name, client_encoding, end marker.
* One additional slot to set passfile and 4 slots for ssl options. * One additional slot to set passfile and 4 slots for ssl options.
*/ */
option_count = list_length(connection_options) + REMOTE_CONNECTION_SESSION_OPTIONS_N + option_count = list_length(connection_options) + REMOTE_CONNECTION_OPTIONS_TOTAL_N;
REMOTE_CONNECTION_PASSWORD_OPTIONS_N + REMOTE_CONNECTION_SSL_OPTIONS_N;
keywords = (const char **) palloc(option_count * sizeof(char *)); keywords = (const char **) palloc(option_count * sizeof(char *));
values = (const char **) palloc(option_count * sizeof(char *)); values = (const char **) palloc(option_count * sizeof(char *));
option_pos = extract_connection_options(connection_options, keywords, values, &user_name); option_pos = extract_connection_options(connection_options, keywords, values, &user_name);
if (NULL == user_name)
user_name = GetUserNameFromId(GetUserId(), false);
/* Use the extension name as fallback_application_name. */ /* Use the extension name as fallback_application_name. */
keywords[option_pos] = "fallback_application_name"; keywords[option_pos] = "fallback_application_name";
values[option_pos] = EXTENSION_NAME; values[option_pos] = EXTENSION_NAME;
@ -1392,6 +1397,22 @@ get_user_mapping(Oid userid, Oid serverid)
return um; return um;
} }
static bool
options_contain(List *options, const char *key)
{
ListCell *lc;
foreach (lc, options)
{
DefElem *d = (DefElem *) lfirst(lc);
if (strcmp(d->defname, key) == 0)
return true;
}
return false;
}
/* /*
* Add user info (username and optionally password) to the connection * Add user info (username and optionally password) to the connection
* options). * options).
@ -1399,19 +1420,23 @@ get_user_mapping(Oid userid, Oid serverid)
static List * static List *
add_userinfo_to_server_options(ForeignServer *server, Oid user_id) add_userinfo_to_server_options(ForeignServer *server, Oid user_id)
{ {
const char *user_name = GetUserNameFromId(user_id, false);
List *server_options = list_copy(server->options);
const UserMapping *um = get_user_mapping(user_id, server->serverid); const UserMapping *um = get_user_mapping(user_id, server->serverid);
List *options = list_copy(server->options);
/* If a user mapping exists, then use the "user" and "password" options /* If a user mapping exists, then use the "user" and "password" options
* from the user mapping (we assume that these options exist, or the * from the user mapping (we assume that these options exist, or the
* connection will later fail). Otherwise, just add the "user" and rely on * connection will later fail). Otherwise, just add the "user" and rely on
* other authentication mechanisms. */ * other authentication mechanisms. */
if (NULL != um) if (NULL != um)
return list_concat(server_options, um->options); options = list_concat(options, um->options);
return lappend(server_options, if (!options_contain(options, "user"))
makeDefElem("user", (Node *) makeString(pstrdup(user_name)), -1)); {
char *user_name = GetUserNameFromId(user_id, false);
options = lappend(options, makeDefElem("user", (Node *) makeString(user_name), -1));
}
return options;
} }
TSConnection * TSConnection *

View File

@ -752,7 +752,9 @@ NOTICE: adding not-null constraint to column "time"
ERROR: could not connect to "data2" ERROR: could not connect to "data2"
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
RESET ROLE; RESET ROLE;
CREATE USER MAPPING FOR :ROLE_3 SERVER data2 OPTIONS (user :'ROLE_3', password :'ROLE_3_PASS'); -- Create user mapping for ROLE_3, but don't specify user in
-- options. The "current user" will instead be used when connecting.
CREATE USER MAPPING FOR :ROLE_3 SERVER data2 OPTIONS (password :'ROLE_3_PASS');
SET ROLE :ROLE_3; SET ROLE :ROLE_3;
-- User should be able to connect and create the distributed -- User should be able to connect and create the distributed
-- hypertable at this point. -- hypertable at this point.
@ -771,3 +773,5 @@ SELECT * FROM disttable_role_3;
Tue Jan 01 00:00:00 2019 PST | 1 | 23.4 Tue Jan 01 00:00:00 2019 PST | 1 | 23.4
(1 row) (1 row)
DROP USER MAPPING FOR :ROLE_3 SERVER data1;
DROP USER MAPPING FOR :ROLE_3 SERVER data2;

View File

@ -262,7 +262,9 @@ SELECT * FROM create_distributed_hypertable('disttable_role_3', 'time', data_nod
\set ON_ERROR_STOP 1 \set ON_ERROR_STOP 1
RESET ROLE; RESET ROLE;
CREATE USER MAPPING FOR :ROLE_3 SERVER data2 OPTIONS (user :'ROLE_3', password :'ROLE_3_PASS'); -- Create user mapping for ROLE_3, but don't specify user in
-- options. The "current user" will instead be used when connecting.
CREATE USER MAPPING FOR :ROLE_3 SERVER data2 OPTIONS (password :'ROLE_3_PASS');
SET ROLE :ROLE_3; SET ROLE :ROLE_3;
-- User should be able to connect and create the distributed -- User should be able to connect and create the distributed
@ -273,3 +275,5 @@ SELECT * FROM create_distributed_hypertable('disttable_role_3', 'time', data_nod
INSERT INTO disttable_role_3 VALUES ('2019-01-01 00:00:00', 1, 23.4); INSERT INTO disttable_role_3 VALUES ('2019-01-01 00:00:00', 1, 23.4);
SELECT * FROM disttable_role_3; SELECT * FROM disttable_role_3;
DROP USER MAPPING FOR :ROLE_3 SERVER data1;
DROP USER MAPPING FOR :ROLE_3 SERVER data2;