Add password parameter to add_server()

Establishing a remote connection requires a password, unless the
connection is made as a superuser. Therefore, this change adds the
option to specify a password in the `add_server()` command.  This is a
required parameter unless called as a superuser.
This commit is contained in:
Erik Nordström 2019-01-25 10:03:57 +01:00 committed by Erik Nordström
parent 652bb26415
commit 125f793307
6 changed files with 99 additions and 48 deletions

View File

@ -137,6 +137,8 @@ AS '@MODULE_PATHNAME@', 'ts_tablespace_detach_all_from_hypertable' LANGUAGE C VO
CREATE OR REPLACE FUNCTION show_tablespaces(hypertable REGCLASS) RETURNS SETOF NAME
AS '@MODULE_PATHNAME@', 'ts_tablespace_show' LANGUAGE C VOLATILE STRICT;
-- Add a server to a TimescaleDB cluster. This also add a
-- corresponding user mapping, if one does not already exist.
CREATE OR REPLACE FUNCTION add_server(
server_name NAME,
host TEXT = 'localhost',
@ -144,10 +146,12 @@ CREATE OR REPLACE FUNCTION add_server(
port INTEGER = 5432,
local_user REGROLE = NULL,
remote_user NAME = NULL,
password TEXT = NULL,
if_not_exists BOOLEAN = FALSE
) RETURNS TABLE(server_name NAME, host TEXT, port INTEGER, database NAME, username NAME, server_username NAME, created BOOL)
AS '@MODULE_PATHNAME@', 'ts_server_add' LANGUAGE C VOLATILE;
-- Delete a server from a TimescaleDB cluster
CREATE OR REPLACE FUNCTION delete_server(
server_name NAME,
if_exists BOOLEAN = FALSE,

View File

@ -13,16 +13,17 @@
#include <commands/dbcommands.h>
#include <commands/defrem.h>
#include <utils/builtins.h>
#include <libpq/crypt.h>
#include <miscadmin.h>
#include <funcapi.h>
#include <hypertable_server.h>
#include <compat.h>
#include <catalog.h>
#include "fdw/timescaledb_fdw.h"
#include "server.h"
#include "compat.h"
#include "catalog.h"
#define TS_FOREIGN_DATA_WRAPPER_NAME "timescaledb_fdw"
#define TS_DEFAULT_POSTGRES_PORT 5432
#define TS_DEFAULT_POSTGRES_HOST "localhost"
@ -30,10 +31,12 @@
* Create a user mapping.
*
* Returns the OID of the created user mapping.
*
* Non-superusers must provide a password.
*/
static Oid
create_user_mapping(const char *username, const char *server_username, const char *servername,
bool if_not_exists)
const char *password, bool if_not_exists)
{
ObjectAddress objaddr;
RoleSpec rolespec = {
@ -51,10 +54,27 @@ create_user_mapping(const char *username, const char *server_username, const cha
.if_not_exists = if_not_exists,
#endif
.servername = (char *) servername,
.options = list_make1(
makeDefElemCompat("user", (Node *) makeString(pstrdup(server_username)), -1)),
.options = NIL,
};
Assert(NULL != username && NULL != server_username && NULL != servername);
stmt.options =
list_make1(makeDefElemCompat("user", (Node *) makeString(pstrdup(server_username)), -1));
/* Non-superusers must provide a password */
if (!superuser() && (NULL == password || password[0] == '\0'))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("no password specified for user \"%s\"", server_username),
errhint("Specify a password to use when connecting to server \"%s\"",
servername)));
if (NULL != password)
stmt.options =
lappend(stmt.options,
makeDefElemCompat("password", (Node *) makeString(pstrdup(password)), -1));
objaddr = CreateUserMapping(&stmt);
return objaddr.objectId;
@ -73,7 +93,7 @@ create_foreign_server(const char *servername, const char *host, int32 port, cons
CreateForeignServerStmt stmt = {
.type = T_CreateForeignServerStmt,
.servername = (char *) servername,
.fdwname = TS_FOREIGN_DATA_WRAPPER_NAME,
.fdwname = TIMESCALEDB_FDW_NAME,
.options =
list_make3(makeDefElemCompat("host", (Node *) makeString(pstrdup(host)), -1),
makeDefElemCompat("port", (Node *) makeInteger(port), -1),
@ -170,7 +190,8 @@ server_add(PG_FUNCTION_ARGS)
Oid userid = PG_ARGISNULL(4) ? GetUserId() : PG_GETARG_OID(4);
const char *server_username =
PG_ARGISNULL(5) ? GetUserNameFromId(userid, false) : PG_GETARG_CSTRING(5);
bool if_not_exists = PG_ARGISNULL(6) ? false : PG_GETARG_BOOL(6);
const char *password = PG_ARGISNULL(6) ? NULL : TextDatumGetCString(PG_GETARG_DATUM(6));
bool if_not_exists = PG_ARGISNULL(7) ? false : PG_GETARG_BOOL(7);
ForeignServer *server;
UserMapping *um;
const char *username;
@ -221,7 +242,7 @@ server_add(PG_FUNCTION_ARGS)
if (!created)
elog(NOTICE, "adding user mapping for \"%s\" to server \"%s\"", username, servername);
create_user_mapping(username, server_username, servername, if_not_exists);
create_user_mapping(username, server_username, servername, password, if_not_exists);
/* Make user mapping visible */
CommandCounterIncrement();
@ -288,7 +309,7 @@ server_get_servername_list(void)
ScanKeyData scankey[1];
SysScanDesc scandesc;
Relation rel;
ForeignDataWrapper *fdw = GetForeignDataWrapperByName(TS_FOREIGN_DATA_WRAPPER_NAME, false);
ForeignDataWrapper *fdw = GetForeignDataWrapperByName(TIMESCALEDB_FDW_NAME, false);
List *servers = NIL;
rel = table_open(ForeignServerRelationId, AccessShareLock);

View File

@ -3,25 +3,27 @@
-- LICENSE-TIMESCALE for a copy of the license.
-- Need to be super user to create extension and add servers
\c :TEST_DBNAME :ROLE_SUPERUSER;
-- Need explicit password for non-super users to connect
ALTER ROLE :ROLE_DEFAULT_PERM_USER PASSWORD 'perm_user_pass';
GRANT USAGE ON FOREIGN DATA WRAPPER timescaledb_fdw TO :ROLE_DEFAULT_PERM_USER;
SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Add servers using TimescaleDB server management API
SELECT * FROM add_server('server_1');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+---------------------------+-------------------+-------------------+---------
server_1 | localhost | 5432 | db_hypertable_distributed | default_perm_user | default_perm_user | t
SELECT * FROM add_server('server_1', database => 'server_1', password => 'perm_user_pass');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+----------+-------------------+-------------------+---------
server_1 | localhost | 5432 | server_1 | default_perm_user | default_perm_user | t
(1 row)
SELECT * FROM add_server('server_2');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+---------------------------+-------------------+-------------------+---------
server_2 | localhost | 5432 | db_hypertable_distributed | default_perm_user | default_perm_user | t
SELECT * FROM add_server('server_2', database => 'server_2', password => 'perm_user_pass');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+----------+-------------------+-------------------+---------
server_2 | localhost | 5432 | server_2 | default_perm_user | default_perm_user | t
(1 row)
SELECT * FROM add_server('server_3');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+---------------------------+-------------------+-------------------+---------
server_3 | localhost | 5432 | db_hypertable_distributed | default_perm_user | default_perm_user | t
SELECT * FROM add_server('server_3', database => 'server_3', password => 'perm_user_pass');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+----------+-------------------+-------------------+---------
server_3 | localhost | 5432 | server_3 | default_perm_user | default_perm_user | t
(1 row)
-- Create a distributed hypertable. Add a trigger and primary key

View File

@ -3,6 +3,7 @@
-- LICENSE-TIMESCALE for a copy of the license.
-- Need to be super user to create extension and add servers
\c :TEST_DBNAME :ROLE_SUPERUSER;
ALTER ROLE :ROLE_DEFAULT_PERM_USER PASSWORD 'perm_user_pass';
GRANT USAGE ON FOREIGN DATA WRAPPER timescaledb_fdw TO :ROLE_DEFAULT_PERM_USER;
CREATE OR REPLACE FUNCTION show_servers()
RETURNS TABLE(server_name NAME, host TEXT, port INT, dbname NAME)
@ -16,7 +17,7 @@ OPTIONS (host 'localhost', port '5432', dbname 'server_1');
-- Create a user mapping for the server
CREATE USER MAPPING FOR :ROLE_SUPERUSER SERVER server_1 OPTIONS (user 'cluster_user_1');
-- Add servers using TimescaleDB server management API
SELECT * FROM add_server('server_2');
SELECT * FROM add_server('server_2', password => 'perm_user_pass');
server_name | host | port | database | username | server_username | created
-------------+-----------+------+-----------+-------------------+-------------------+---------
server_2 | localhost | 5432 | db_server | default_perm_user | default_perm_user | t
@ -24,30 +25,36 @@ SELECT * FROM add_server('server_2');
\set ON_ERROR_STOP 0
-- Add again
SELECT * FROM add_server('server_2');
SELECT * FROM add_server('server_2', password => 'perm_user_pass');
ERROR: server "server_2" already exists
-- Add without password
SELECT * FROM add_server('server_3');
ERROR: no password specified for user "default_perm_user"
-- Add NULL server
SELECT * FROM add_server(NULL);
ERROR: invalid server name
\set ON_ERROR_STOP 1
-- Should not generate error with if_not_exists option
SELECT * FROM add_server('server_2', if_not_exists => true);
SELECT * FROM add_server('server_2', password => 'perm_user_pass', if_not_exists => true);
server_name | host | port | database | username | server_username | created
-------------+-----------+------+-----------+-------------------+-------------------+---------
server_2 | localhost | 5432 | db_server | default_perm_user | default_perm_user | f
(1 row)
RESET ROLE;
-- Superuser requires no password
SELECT * FROM add_server('server_3', host => '192.168.3.4', database => 'server_2', remote_user => 'cluster_user_2');
server_name | host | port | database | username | server_username | created
-------------+-------------+------+----------+-------------------+-----------------+---------
server_3 | 192.168.3.4 | 5432 | server_2 | default_perm_user | cluster_user_2 | t
server_name | host | port | database | username | server_username | created
-------------+-------------+------+----------+------------+-----------------+---------
server_3 | 192.168.3.4 | 5432 | server_2 | super_user | cluster_user_2 | t
(1 row)
SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Server exists, but no user mapping
CREATE SERVER server_4 FOREIGN DATA WRAPPER timescaledb_fdw
OPTIONS (host 'localhost', port '5432', dbname 'server_4');
-- User mapping should be added with NOTICE
SELECT * FROM add_server('server_4', if_not_exists => true);
SELECT * FROM add_server('server_4', password => 'perm_user_pass', if_not_exists => true);
NOTICE: adding user mapping for "default_perm_user" to server "server_4"
server_name | host | port | database | username | server_username | created
-------------+-----------+------+-----------+-------------------+-------------------+---------
@ -92,28 +99,34 @@ SELECT rolname, srvname, umoptions
FROM pg_user_mapping um, pg_authid a, pg_foreign_server fs
WHERE a.oid = um.umuser AND fs.oid = um.umserver
ORDER BY srvname;
rolname | srvname | umoptions
-------------------+----------+--------------------------
rolname | srvname | umoptions
-------------------+----------+--------------------------------------------------
super_user | server_1 | {user=cluster_user_1}
default_perm_user | server_2 | {user=default_perm_user}
default_perm_user | server_3 | {user=cluster_user_2}
default_perm_user | server_4 | {user=default_perm_user}
default_perm_user | server_2 | {user=default_perm_user,password=perm_user_pass}
super_user | server_3 | {user=cluster_user_2}
default_perm_user | server_4 | {user=default_perm_user,password=perm_user_pass}
(4 rows)
SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Delete a server
\set ON_ERROR_STOP 0
-- Cannot delete if not owner
SELECT * FROM delete_server('server_3');
ERROR: must be owner of foreign server server_3
-- Must use cascade because of user mappings
RESET ROLE;
SELECT * FROM delete_server('server_3');
ERROR: cannot drop server server_3 because other objects depend on it
\set ON_ERROR_STOP 1
-- Should work as superuser with cascade
SELECT * FROM delete_server('server_3', cascade => true);
NOTICE: drop cascades to user mapping for default_perm_user on server server_3
NOTICE: drop cascades to user mapping for super_user on server server_3
delete_server
---------------
t
(1 row)
SET ROLE :ROLE_DEFAULT_PERM_USER;
SELECT srvname, srvoptions
FROM pg_foreign_server;
srvname | srvoptions
@ -128,11 +141,11 @@ SELECT rolname, srvname, umoptions
FROM pg_user_mapping um, pg_authid a, pg_foreign_server fs
WHERE a.oid = um.umuser AND fs.oid = um.umserver
ORDER BY srvname;
rolname | srvname | umoptions
-------------------+----------+--------------------------
rolname | srvname | umoptions
-------------------+----------+--------------------------------------------------
super_user | server_1 | {user=cluster_user_1}
default_perm_user | server_2 | {user=default_perm_user}
default_perm_user | server_4 | {user=default_perm_user}
default_perm_user | server_2 | {user=default_perm_user,password=perm_user_pass}
default_perm_user | server_4 | {user=default_perm_user,password=perm_user_pass}
(3 rows)
\set ON_ERROR_STOP 0

View File

@ -4,13 +4,15 @@
-- Need to be super user to create extension and add servers
\c :TEST_DBNAME :ROLE_SUPERUSER;
-- Need explicit password for non-super users to connect
ALTER ROLE :ROLE_DEFAULT_PERM_USER PASSWORD 'perm_user_pass';
GRANT USAGE ON FOREIGN DATA WRAPPER timescaledb_fdw TO :ROLE_DEFAULT_PERM_USER;
SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Add servers using TimescaleDB server management API
SELECT * FROM add_server('server_1');
SELECT * FROM add_server('server_2');
SELECT * FROM add_server('server_3');
SELECT * FROM add_server('server_1', database => 'server_1', password => 'perm_user_pass');
SELECT * FROM add_server('server_2', database => 'server_2', password => 'perm_user_pass');
SELECT * FROM add_server('server_3', database => 'server_3', password => 'perm_user_pass');
-- Create a distributed hypertable. Add a trigger and primary key
-- constraint to test how those work

View File

@ -4,6 +4,7 @@
-- Need to be super user to create extension and add servers
\c :TEST_DBNAME :ROLE_SUPERUSER;
ALTER ROLE :ROLE_DEFAULT_PERM_USER PASSWORD 'perm_user_pass';
GRANT USAGE ON FOREIGN DATA WRAPPER timescaledb_fdw TO :ROLE_DEFAULT_PERM_USER;
CREATE OR REPLACE FUNCTION show_servers()
@ -22,27 +23,31 @@ OPTIONS (host 'localhost', port '5432', dbname 'server_1');
CREATE USER MAPPING FOR :ROLE_SUPERUSER SERVER server_1 OPTIONS (user 'cluster_user_1');
-- Add servers using TimescaleDB server management API
SELECT * FROM add_server('server_2');
SELECT * FROM add_server('server_2', password => 'perm_user_pass');
\set ON_ERROR_STOP 0
-- Add again
SELECT * FROM add_server('server_2');
SELECT * FROM add_server('server_2', password => 'perm_user_pass');
-- Add without password
SELECT * FROM add_server('server_3');
-- Add NULL server
SELECT * FROM add_server(NULL);
\set ON_ERROR_STOP 1
-- Should not generate error with if_not_exists option
SELECT * FROM add_server('server_2', if_not_exists => true);
SELECT * FROM add_server('server_2', password => 'perm_user_pass', if_not_exists => true);
RESET ROLE;
-- Superuser requires no password
SELECT * FROM add_server('server_3', host => '192.168.3.4', database => 'server_2', remote_user => 'cluster_user_2');
SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Server exists, but no user mapping
CREATE SERVER server_4 FOREIGN DATA WRAPPER timescaledb_fdw
OPTIONS (host 'localhost', port '5432', dbname 'server_4');
-- User mapping should be added with NOTICE
SELECT * FROM add_server('server_4', if_not_exists => true);
SELECT * FROM add_server('server_4', password => 'perm_user_pass', if_not_exists => true);
SELECT * FROM show_servers();
@ -66,11 +71,15 @@ SET ROLE :ROLE_DEFAULT_PERM_USER;
-- Delete a server
\set ON_ERROR_STOP 0
-- Cannot delete if not owner
SELECT * FROM delete_server('server_3');
-- Must use cascade because of user mappings
RESET ROLE;
SELECT * FROM delete_server('server_3');
\set ON_ERROR_STOP 1
-- Should work as superuser with cascade
SELECT * FROM delete_server('server_3', cascade => true);
SET ROLE :ROLE_DEFAULT_PERM_USER;
SELECT srvname, srvoptions
FROM pg_foreign_server;