Prefix all objects created with cdb_fdw_
Build the DB objects related to a user FDW with the following form: `cdb_fdw_name`. This is aimed at easily inspect and filter them. As requested in code review.
This commit is contained in:
@@ -139,6 +139,15 @@ $$
|
|||||||
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||||
|
|
||||||
|
|
||||||
|
-- Produce a valid DB name for objects created for the user FDW's
|
||||||
|
CREATE OR REPLACE FUNCTION @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name NAME)
|
||||||
|
RETURNS NAME AS $$
|
||||||
|
-- Note on input we use %s and on output we use %I, in order to
|
||||||
|
-- avoid double escaping
|
||||||
|
SELECT format('cdb_fdw_%s', fdw_input_name)::NAME;
|
||||||
|
$$
|
||||||
|
LANGUAGE sql IMMUTABLE PARALLEL SAFE;
|
||||||
|
|
||||||
-- A function to set up a user-defined foreign data server
|
-- A function to set up a user-defined foreign data server
|
||||||
-- It does not read from CDB_Conf.
|
-- It does not read from CDB_Conf.
|
||||||
-- Only superuser roles can invoke it successfully
|
-- Only superuser roles can invoke it successfully
|
||||||
@@ -159,22 +168,23 @@ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
|||||||
--
|
--
|
||||||
-- Underneath it will:
|
-- Underneath it will:
|
||||||
-- * Set up postgresql_fdw
|
-- * Set up postgresql_fdw
|
||||||
-- * Create a server with the name 'amazon'
|
-- * Create a server with the name 'cdb_fdw_amazon'
|
||||||
-- * Create a role called 'amazon' to manage access
|
-- * Create a role called 'cdb_fdw_amazon' to manage access
|
||||||
-- * Create a user mapping with that role 'amazon'
|
-- * Create a user mapping with that role 'cdb_fdw_amazon'
|
||||||
-- * Create a schema 'amazon' as a convenience to set up all foreign
|
-- * Create a schema 'cdb_fdw_amazon' as a convenience to set up all foreign
|
||||||
-- tables over there
|
-- tables over there
|
||||||
--
|
--
|
||||||
-- It is the responsibility of the superuser to grant that role to either:
|
-- It is the responsibility of the superuser to grant that role to either:
|
||||||
-- * Nobody
|
-- * Nobody
|
||||||
-- * Specific roles: GRANT amazon TO role_name;
|
-- * Specific roles: GRANT amazon TO role_name;
|
||||||
-- * Members of the organization: SELECT cartodb.CDB_Organization_Grant_Role('amazon');
|
-- * Members of the organization: SELECT cartodb.CDB_Organization_Grant_Role('cdb_fdw_amazon');
|
||||||
-- * The publicuser: GRANT amazon TO publicuser;
|
-- * The publicuser: GRANT cdb_fdw_amazon TO publicuser;
|
||||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_SetUp_User_PG_FDW_Server(fdw_name NAME, config json)
|
CREATE OR REPLACE FUNCTION @extschema@._CDB_SetUp_User_PG_FDW_Server(fdw_input_name NAME, config json)
|
||||||
RETURNS void AS $$
|
RETURNS void AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
row record;
|
row record;
|
||||||
option record;
|
option record;
|
||||||
|
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||||
BEGIN
|
BEGIN
|
||||||
-- TODO: refactor with original function
|
-- TODO: refactor with original function
|
||||||
-- This function tries to be as idempotent as possible, by not creating anything more than once
|
-- This function tries to be as idempotent as possible, by not creating anything more than once
|
||||||
@@ -183,57 +193,57 @@ BEGIN
|
|||||||
CREATE EXTENSION postgres_fdw;
|
CREATE EXTENSION postgres_fdw;
|
||||||
END IF;
|
END IF;
|
||||||
-- Create FDW first if it does not exist
|
-- Create FDW first if it does not exist
|
||||||
IF NOT EXISTS ( SELECT * FROM pg_foreign_server WHERE srvname = fdw_name)
|
IF NOT EXISTS ( SELECT * FROM pg_foreign_server WHERE srvname = fdw_objects_name)
|
||||||
THEN
|
THEN
|
||||||
EXECUTE FORMAT('CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw', fdw_name);
|
EXECUTE FORMAT('CREATE SERVER %I FOREIGN DATA WRAPPER postgres_fdw', fdw_objects_name);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Set FDW settings
|
-- Set FDW settings
|
||||||
FOR row IN SELECT p.key, p.value from lateral json_each_text(config->'server') p
|
FOR row IN SELECT p.key, p.value from lateral json_each_text(config->'server') p
|
||||||
LOOP
|
LOOP
|
||||||
IF NOT EXISTS (WITH a AS (select split_part(unnest(srvoptions), '=', 1) as options from pg_foreign_server where srvname=fdw_name) SELECT * from a where options = row.key)
|
IF NOT EXISTS (WITH a AS (select split_part(unnest(srvoptions), '=', 1) as options from pg_foreign_server where srvname=fdw_objects_name) SELECT * from a where options = row.key)
|
||||||
THEN
|
THEN
|
||||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (ADD %I %L)', fdw_name, row.key, row.value);
|
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (ADD %I %L)', fdw_objects_name, row.key, row.value);
|
||||||
ELSE
|
ELSE
|
||||||
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (SET %I %L)', fdw_name, row.key, row.value);
|
EXECUTE FORMAT('ALTER SERVER %I OPTIONS (SET %I %L)', fdw_objects_name, row.key, row.value);
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
-- Create specific role for this
|
-- Create specific role for this
|
||||||
IF NOT EXISTS ( SELECT 1 FROM pg_roles WHERE rolname = fdw_name) THEN
|
IF NOT EXISTS ( SELECT 1 FROM pg_roles WHERE rolname = fdw_objects_name) THEN
|
||||||
EXECUTE format('CREATE ROLE %I NOLOGIN', fdw_name);
|
EXECUTE format('CREATE ROLE %I NOLOGIN', fdw_objects_name);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Transfer ownership of the server to the fdw role
|
-- Transfer ownership of the server to the fdw role
|
||||||
EXECUTE format('ALTER SERVER %I OWNER TO %I', fdw_name, fdw_name);
|
EXECUTE format('ALTER SERVER %I OWNER TO %I', fdw_objects_name, fdw_objects_name);
|
||||||
|
|
||||||
-- Create user mapping
|
-- Create user mapping
|
||||||
-- NOTE: we use a PUBLIC user mapping but control access to the SERVER
|
-- NOTE: we use a PUBLIC user mapping but control access to the SERVER
|
||||||
-- so that we don't need to create a mapping for every user nor store credentials elsewhere
|
-- so that we don't need to create a mapping for every user nor store credentials elsewhere
|
||||||
IF NOT EXISTS ( SELECT * FROM pg_user_mappings WHERE srvname = fdw_name AND usename = 'public' ) THEN
|
IF NOT EXISTS ( SELECT * FROM pg_user_mappings WHERE srvname = fdw_objects_name AND usename = 'public' ) THEN
|
||||||
EXECUTE FORMAT ('CREATE USER MAPPING FOR public SERVER %I', fdw_name);
|
EXECUTE FORMAT ('CREATE USER MAPPING FOR public SERVER %I', fdw_objects_name);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Update user mapping settings
|
-- Update user mapping settings
|
||||||
FOR option IN SELECT o.key, o.value from lateral json_each_text(config->'user_mapping') o LOOP
|
FOR option IN SELECT o.key, o.value from lateral json_each_text(config->'user_mapping') o LOOP
|
||||||
IF NOT EXISTS (WITH a AS (select split_part(unnest(umoptions), '=', 1) as options from pg_user_mappings WHERE srvname = fdw_name AND usename = 'public') SELECT * from a where options = option.key) THEN
|
IF NOT EXISTS (WITH a AS (select split_part(unnest(umoptions), '=', 1) as options from pg_user_mappings WHERE srvname = fdw_objects_name AND usename = 'public') SELECT * from a where options = option.key) THEN
|
||||||
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (ADD %I %L)', fdw_name, option.key, option.value);
|
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (ADD %I %L)', fdw_objects_name, option.key, option.value);
|
||||||
ELSE
|
ELSE
|
||||||
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (SET %I %L)', fdw_name, option.key, option.value);
|
EXECUTE FORMAT('ALTER USER MAPPING FOR PUBLIC SERVER %I OPTIONS (SET %I %L)', fdw_objects_name, option.key, option.value);
|
||||||
END IF;
|
END IF;
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
|
||||||
-- Grant usage on the wrapper and server to the fdw role
|
-- Grant usage on the wrapper and server to the fdw role
|
||||||
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO %I', fdw_name);
|
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO %I', fdw_objects_name);
|
||||||
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN SERVER %I TO %I', fdw_name, fdw_name);
|
EXECUTE FORMAT ('GRANT USAGE ON FOREIGN SERVER %I TO %I', fdw_objects_name, fdw_objects_name);
|
||||||
|
|
||||||
-- Create schema if it does not exist.
|
-- Create schema if it does not exist.
|
||||||
IF NOT EXISTS ( SELECT * from pg_namespace WHERE nspname=fdw_name) THEN
|
IF NOT EXISTS ( SELECT * from pg_namespace WHERE nspname=fdw_objects_name) THEN
|
||||||
EXECUTE FORMAT ('CREATE SCHEMA %I', fdw_name);
|
EXECUTE FORMAT ('CREATE SCHEMA %I', fdw_objects_name);
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Give the fdw role ownership over the schema
|
-- Give the fdw role ownership over the schema
|
||||||
EXECUTE FORMAT ('ALTER SCHEMA %I OWNER TO %I', fdw_name, fdw_name);
|
EXECUTE FORMAT ('ALTER SCHEMA %I OWNER TO %I', fdw_objects_name, fdw_objects_name);
|
||||||
|
|
||||||
-- TODO: Bring here the remote cdb_tablemetadata
|
-- TODO: Bring here the remote cdb_tablemetadata
|
||||||
END
|
END
|
||||||
@@ -248,14 +258,16 @@ $$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
|||||||
-- SELECT cartodb.CDB_Drop_User_PG_FDW_Server('amazon')
|
-- SELECT cartodb.CDB_Drop_User_PG_FDW_Server('amazon')
|
||||||
--
|
--
|
||||||
-- Note: if there's any dependent object (i.e. foreign table) this call will fail
|
-- Note: if there's any dependent object (i.e. foreign table) this call will fail
|
||||||
CREATE OR REPLACE FUNCTION @extschema@._CDB_Drop_User_PG_FDW_Server(fdw_name NAME)
|
CREATE OR REPLACE FUNCTION @extschema@._CDB_Drop_User_PG_FDW_Server(fdw_input_name NAME)
|
||||||
RETURNS void AS $$
|
RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT ('DROP SCHEMA %I', fdw_name);
|
EXECUTE FORMAT ('DROP SCHEMA %I', fdw_objects_name);
|
||||||
EXECUTE FORMAT ('DROP USER MAPPING FOR public SERVER %I', fdw_name);
|
EXECUTE FORMAT ('DROP USER MAPPING FOR public SERVER %I', fdw_objects_name);
|
||||||
EXECUTE FORMAT ('DROP SERVER %I', fdw_name);
|
EXECUTE FORMAT ('DROP SERVER %I', fdw_objects_name);
|
||||||
EXECUTE FORMAT ('REVOKE USAGE ON FOREIGN DATA WRAPPER postgres_fdw FROM %I', fdw_name);
|
EXECUTE FORMAT ('REVOKE USAGE ON FOREIGN DATA WRAPPER postgres_fdw FROM %I', fdw_objects_name);
|
||||||
EXECUTE FORMAT ('DROP ROLE %I', fdw_name);
|
EXECUTE FORMAT ('DROP ROLE %I', fdw_objects_name);
|
||||||
END
|
END
|
||||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||||
|
|
||||||
@@ -264,12 +276,14 @@ $$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
|||||||
-- E.g:
|
-- E.g:
|
||||||
-- SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('amazon', 'carto_lite', 'mytable');
|
-- SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('amazon', 'carto_lite', 'mytable');
|
||||||
-- SELECT * FROM amazon.my_table;
|
-- SELECT * FROM amazon.my_table;
|
||||||
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUp_User_PG_FDW_Table(fdw_name NAME, foreign_schema NAME, table_name NAME)
|
CREATE OR REPLACE FUNCTION @extschema@.CDB_SetUp_User_PG_FDW_Table(fdw_input_name NAME, foreign_schema NAME, table_name NAME)
|
||||||
RETURNS void AS $$
|
RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
fdw_objects_name NAME := @extschema@.__CDB_User_FDW_Object_Names(fdw_input_name);
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT ('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I;', foreign_schema, table_name, fdw_name, fdw_name);
|
EXECUTE FORMAT ('IMPORT FOREIGN SCHEMA %I LIMIT TO (%I) FROM SERVER %I INTO %I;', foreign_schema, table_name, fdw_objects_name, fdw_objects_name);
|
||||||
--- Grant SELECT to fdw role
|
--- Grant SELECT to fdw role
|
||||||
EXECUTE FORMAT ('GRANT SELECT ON %I.%I TO %I;', fdw_name, table_name, fdw_name);
|
EXECUTE FORMAT ('GRANT SELECT ON %I.%I TO %I;', fdw_objects_name, table_name, fdw_objects_name);
|
||||||
END
|
END
|
||||||
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
$$ LANGUAGE plpgsql VOLATILE PARALLEL UNSAFE;
|
||||||
|
|
||||||
|
|||||||
+21
-21
@@ -607,43 +607,43 @@ test_extension|public|"local-table-with-dashes"'
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
sql postgres "SELECT cartodb._CDB_SetUp_User_PG_FDW_Server('test_user_fdw', '$ufdw_config');"
|
sql postgres "SELECT cartodb._CDB_SetUp_User_PG_FDW_Server('user_defined_test', '$ufdw_config');"
|
||||||
|
|
||||||
# Grant a user access to that FDW, and to grant to others
|
# Grant a user access to that FDW, and to grant to others
|
||||||
sql postgres "GRANT test_user_fdw TO cdb_testmember_1 WITH ADMIN OPTION;"
|
sql postgres "GRANT cdb_fdw_user_defined_test TO cdb_testmember_1 WITH ADMIN OPTION;"
|
||||||
|
|
||||||
# Set up a user foreign table
|
# Set up a user foreign table
|
||||||
sql cdb_testmember_1 "SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('test_user_fdw', 'test_fdw', 'foo');"
|
sql cdb_testmember_1 "SELECT cartodb.CDB_SetUp_User_PG_FDW_Table('user_defined_test', 'test_fdw', 'foo');"
|
||||||
|
|
||||||
# Check that the table can be accessed by the owner/creator
|
# Check that the table can be accessed by the owner/creator
|
||||||
sql cdb_testmember_1 "SELECT * from test_user_fdw.foo;"
|
sql cdb_testmember_1 "SELECT * from cdb_fdw_user_defined_test.foo;"
|
||||||
sql cdb_testmember_1 "SELECT a from test_user_fdw.foo LIMIT 1;" should 42
|
sql cdb_testmember_1 "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" should 42
|
||||||
|
|
||||||
# Check that a role with no permissions cannot use the FDW to access a remote table
|
# Check that a role with no permissions cannot use the FDW to access a remote table
|
||||||
sql cdb_testmember_2 "IMPORT FOREIGN SCHEMA test_fdw LIMIT TO (foo) FROM SERVER test_user_fdw INTO public" fails
|
sql cdb_testmember_2 "IMPORT FOREIGN SCHEMA test_fdw LIMIT TO (foo) FROM SERVER cdb_fdw_user_defined_test INTO public" fails
|
||||||
|
|
||||||
# Check that the table can be accessed by some other user by granting the role
|
# Check that the table can be accessed by some other user by granting the role
|
||||||
sql cdb_testmember_2 "SELECT a from test_user_fdw.foo LIMIT 1;" fails
|
sql cdb_testmember_2 "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" fails
|
||||||
sql cdb_testmember_1 "GRANT test_user_fdw TO cdb_testmember_2;"
|
sql cdb_testmember_1 "GRANT cdb_fdw_user_defined_test TO cdb_testmember_2;"
|
||||||
sql cdb_testmember_2 "SELECT a from test_user_fdw.foo LIMIT 1;" should 42
|
sql cdb_testmember_2 "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" should 42
|
||||||
sql cdb_testmember_1 "REVOKE test_user_fdw FROM cdb_testmember_2;"
|
sql cdb_testmember_1 "REVOKE cdb_fdw_user_defined_test FROM cdb_testmember_2;"
|
||||||
|
|
||||||
# Check that the table can be accessed by org members
|
# Check that the table can be accessed by org members
|
||||||
sql cdb_testmember_2 "SELECT a from test_user_fdw.foo LIMIT 1;" fails
|
sql cdb_testmember_2 "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" fails
|
||||||
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Grant_Role('test_user_fdw');"
|
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Grant_Role('cdb_fdw_user_defined_test');"
|
||||||
sql cdb_testmember_2 "SELECT a from test_user_fdw.foo LIMIT 1;" should 42
|
sql cdb_testmember_2 "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" should 42
|
||||||
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Revoke_Role('test_user_fdw');"
|
sql cdb_testmember_1 "SELECT cartodb.CDB_Organization_Revoke_Role('cdb_fdw_user_defined_test');"
|
||||||
|
|
||||||
# By default publicuser cannot access the FDW
|
# By default publicuser cannot access the FDW
|
||||||
sql publicuser "SELECT a from test_user_fdw.foo LIMIT 1;" fails
|
sql publicuser "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" fails
|
||||||
sql cdb_testmember_1 "GRANT test_user_fdw TO publicuser;" # but can be granted
|
sql cdb_testmember_1 "GRANT cdb_fdw_user_defined_test TO publicuser;" # but can be granted
|
||||||
sql publicuser "SELECT a from test_user_fdw.foo LIMIT 1;" should 42
|
sql publicuser "SELECT a from cdb_fdw_user_defined_test.foo LIMIT 1;" should 42
|
||||||
sql cdb_testmember_1 "REVOKE test_user_fdw FROM publicuser;"
|
sql cdb_testmember_1 "REVOKE cdb_fdw_user_defined_test FROM publicuser;"
|
||||||
|
|
||||||
# If there are dependent objects, we cannot drop the foreign server
|
# If there are dependent objects, we cannot drop the foreign server
|
||||||
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('test_user_fdw')" fails
|
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('user_defined_test')" fails
|
||||||
sql cdb_testmember_1 "DROP FOREIGN TABLE test_user_fdw.foo;"
|
sql cdb_testmember_1 "DROP FOREIGN TABLE cdb_fdw_user_defined_test.foo;"
|
||||||
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('test_user_fdw')"
|
sql postgres "SELECT cartodb._CDB_Drop_User_PG_FDW_Server('user_defined_test')"
|
||||||
|
|
||||||
|
|
||||||
# Teardown
|
# Teardown
|
||||||
|
|||||||
Reference in New Issue
Block a user