-- -- GRANT_ON_SCHEMA_PROPAGATION -- -- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15; server_version_ge_15 --------------------------------------------------------------------- f (1 row) -- test grants are propagated when the schema is CREATE SCHEMA dist_schema; CREATE TABLE dist_schema.dist_table (id int); CREATE SCHEMA another_dist_schema; CREATE TABLE another_dist_schema.dist_table (id int); SET citus.enable_ddl_propagation TO off; CREATE SCHEMA non_dist_schema; SET citus.enable_ddl_propagation TO on; -- create roles on all nodes CREATE USER role_1; CREATE USER role_2; CREATE USER role_3; -- do some varying grants GRANT USAGE, CREATE ON SCHEMA dist_schema TO role_1 WITH GRANT OPTION; GRANT USAGE ON SCHEMA dist_schema TO role_2; SET ROLE role_1; GRANT USAGE ON SCHEMA dist_schema TO role_3 WITH GRANT OPTION; GRANT CREATE ON SCHEMA dist_schema TO role_3; GRANT CREATE, USAGE ON SCHEMA dist_schema TO PUBLIC; RESET ROLE; GRANT USAGE ON SCHEMA dist_schema TO PUBLIC; SELECT create_distributed_table('dist_schema.dist_table', 'id'); create_distributed_table --------------------------------------------------------------------- (1 row) SELECT create_distributed_table('another_dist_schema.dist_table', 'id'); create_distributed_table --------------------------------------------------------------------- (1 row) SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'dist_schema'; nspname | nspacl --------------------------------------------------------------------- dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U/postgres,role_3=U*C/role_1,=UC/role_1,=U/postgres} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'dist_schema'; nspname | nspacl --------------------------------------------------------------------- dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U/postgres,role_3=U*C/role_1,=UC/role_1,=U/postgres} (1 row) \c - - - :master_port -- grant all permissions GRANT ALL ON SCHEMA dist_schema, another_dist_schema, non_dist_schema TO role_1, role_2, role_3 WITH GRANT OPTION; SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/postgres,role_3=U*C*/postgres} dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/postgres,role_3=U*C/role_1,=UC/role_1,=U/postgres,role_3=U*C*/postgres} non_dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/postgres,role_3=U*C*/postgres} (3 rows) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/postgres,role_3=U*C*/postgres} dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/postgres,role_3=U*C/role_1,=UC/role_1,=U/postgres,role_3=U*C*/postgres} (2 rows) \c - - - :master_port -- revoke all permissions REVOKE ALL ON SCHEMA dist_schema, another_dist_schema, non_dist_schema FROM role_1, role_2, role_3, PUBLIC CASCADE; SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres} dist_schema | {postgres=UC/postgres} non_dist_schema | {postgres=UC/postgres} (3 rows) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres} dist_schema | {postgres=UC/postgres} (2 rows) \c - - - :master_port -- grant with multiple permissions, roles and schemas GRANT USAGE, CREATE ON SCHEMA dist_schema, another_dist_schema, non_dist_schema TO role_1, role_2, role_3; SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_1=UC/postgres,role_2=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_1=UC/postgres,role_2=UC/postgres,role_3=UC/postgres} non_dist_schema | {postgres=UC/postgres,role_1=UC/postgres,role_2=UC/postgres,role_3=UC/postgres} (3 rows) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_1=UC/postgres,role_2=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_1=UC/postgres,role_2=UC/postgres,role_3=UC/postgres} (2 rows) \c - - - :master_port -- revoke with multiple permissions, roles and schemas REVOKE USAGE, CREATE ON SCHEMA dist_schema, another_dist_schema, non_dist_schema FROM role_1, role_2; SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_3=UC/postgres} non_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} (3 rows) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_3=UC/postgres} (2 rows) \c - - - :master_port -- grant with grant option GRANT USAGE ON SCHEMA dist_schema TO role_1, role_3 WITH GRANT OPTION; \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_3=U*C/postgres,role_1=U*/postgres} (2 rows) \c - - - :master_port -- revoke grant option for REVOKE GRANT OPTION FOR USAGE ON SCHEMA dist_schema FROM role_3; \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_3=UC/postgres,role_1=U*/postgres} (2 rows) \c - - - :master_port -- test current_user SET citus.enable_alter_role_propagation TO ON; ALTER ROLE role_1 SUPERUSER; SET citus.enable_alter_role_propagation TO OFF; SET ROLE role_1; -- this is only supported on citus enterprise where multiple users can be managed -- The output of the nspname select below will indicate if the create has been granted GRANT CREATE ON SCHEMA dist_schema TO CURRENT_USER; \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname IN ('dist_schema', 'another_dist_schema', 'non_dist_schema') ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- another_dist_schema | {postgres=UC/postgres,role_3=UC/postgres} dist_schema | {postgres=UC/postgres,role_3=UC/postgres,role_1=U*C/postgres} (2 rows) \c - - - :master_port RESET ROLE; SET citus.enable_alter_role_propagation TO ON; ALTER ROLE role_1 NOSUPERUSER; SET citus.enable_alter_role_propagation TO OFF; DROP TABLE dist_schema.dist_table, another_dist_schema.dist_table; DROP SCHEMA dist_schema; DROP SCHEMA another_dist_schema; DROP SCHEMA non_dist_schema; -- test if the grantors are propagated correctly -- first remove one of the worker nodes SET citus.shard_replication_factor TO 1; SELECT master_remove_node('localhost', :worker_2_port); master_remove_node --------------------------------------------------------------------- (1 row) -- create a new schema CREATE SCHEMA grantor_schema; -- give cascading permissions GRANT USAGE, CREATE ON SCHEMA grantor_schema TO role_1 WITH GRANT OPTION; GRANT CREATE ON SCHEMA grantor_schema TO PUBLIC; SET ROLE role_1; GRANT USAGE ON SCHEMA grantor_schema TO role_2 WITH GRANT OPTION; GRANT CREATE ON SCHEMA grantor_schema TO role_2; GRANT USAGE, CREATE ON SCHEMA grantor_schema TO role_3; GRANT CREATE, USAGE ON SCHEMA grantor_schema TO PUBLIC; SET ROLE role_2; GRANT USAGE ON SCHEMA grantor_schema TO role_3; RESET ROLE; -- distribute the schema CREATE TABLE grantor_schema.grantor_table (id INT); SELECT create_distributed_table('grantor_schema.grantor_table', 'id'); create_distributed_table --------------------------------------------------------------------- (1 row) -- check if the grantors are propagated correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=U*C/role_1,role_3=UC/role_1,=UC/role_1,role_3=U/role_2} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=U*C/role_1,role_3=UC/role_1,=UC/role_1,role_3=U/role_2} (1 row) \c - - - :master_port -- add the previously removed node SELECT 1 FROM master_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) -- check if the grantors are propagated correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=U*C/role_1,role_3=UC/role_1,=UC/role_1,role_3=U/role_2} (1 row) \c - - - :worker_2_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=U*C/role_1,role_3=UC/role_1,=UC/role_1,role_3=U/role_2} (1 row) \c - - - :master_port -- revoke one of the permissions REVOKE USAGE ON SCHEMA grantor_schema FROM role_1 CASCADE; -- check if revoke worked correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=C*/postgres,=C/postgres,role_2=C/role_1,role_3=C/role_1,=C/role_1} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=C*/postgres,=C/postgres,role_2=C/role_1,role_3=C/role_1,=C/role_1} (1 row) \c - - - :master_port -- test if grantor propagates correctly on already distributed schemas GRANT USAGE ON SCHEMA grantor_schema TO role_1 WITH GRANT OPTION; SET ROLE role_1; GRANT USAGE ON SCHEMA grantor_schema TO role_2; GRANT USAGE ON SCHEMA grantor_schema TO role_3 WITH GRANT OPTION; SET ROLE role_3; GRANT USAGE ON SCHEMA grantor_schema TO role_2; RESET ROLE; -- check the results SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=UC/role_1,role_3=U*C/role_1,=C/role_1,role_2=U/role_3} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'grantor_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- grantor_schema | {postgres=UC/postgres,role_1=U*C*/postgres,=C/postgres,role_2=UC/role_1,role_3=U*C/role_1,=C/role_1,role_2=U/role_3} (1 row) \c - - - :master_port DROP TABLE grantor_schema.grantor_table; DROP SCHEMA grantor_schema CASCADE; -- test distributing the schema with another user CREATE SCHEMA dist_schema; GRANT ALL ON SCHEMA dist_schema TO role_1 WITH GRANT OPTION; SET ROLE role_1; GRANT ALL ON SCHEMA dist_schema TO role_2 WITH GRANT OPTION; CREATE TABLE dist_schema.dist_table (id int); SELECT create_distributed_table('dist_schema.dist_table', 'id'); create_distributed_table --------------------------------------------------------------------- (1 row) SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'dist_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/role_1} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'dist_schema' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- dist_schema | {postgres=UC/postgres,role_1=U*C*/postgres,role_2=U*C*/role_1} (1 row) \c - - - :master_port DROP TABLE dist_schema.dist_table; DROP SCHEMA dist_schema CASCADE; -- test grants on public schema -- first remove one of the worker nodes SET citus.shard_replication_factor TO 1; SELECT master_remove_node('localhost', :worker_2_port); master_remove_node --------------------------------------------------------------------- (1 row) -- to avoid different output in PG15 GRANT CREATE ON SCHEMA public TO public; -- distribute the public schema (it has to be distributed by now but just in case) CREATE TABLE public_schema_table (id INT); SELECT create_distributed_table('public_schema_table', 'id'); create_distributed_table --------------------------------------------------------------------- (1 row) -- give cascading permissions GRANT USAGE, CREATE ON SCHEMA PUBLIC TO role_1 WITH GRANT OPTION; SET ROLE role_1; GRANT USAGE ON SCHEMA PUBLIC TO PUBLIC; RESET ROLE; -- check if the grants are propagated correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres,role_1=U*C*/postgres,=U/role_1} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres,role_1=U*C*/postgres,=U/role_1} (1 row) \c - - - :master_port -- add the previously removed node SELECT 1 FROM master_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) -- check if the grants are propagated correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres,role_1=U*C*/postgres,=U/role_1} (1 row) \c - - - :worker_2_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres,role_1=U*C*/postgres,=U/role_1} (1 row) \c - - - :master_port -- revoke those new permissions REVOKE CREATE, USAGE ON SCHEMA PUBLIC FROM role_1 CASCADE; -- check if the grants are propagated correctly SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres} (1 row) \c - - - :worker_1_port SELECT nspname, nspacl FROM pg_namespace WHERE nspname = 'public' ORDER BY nspname; nspname | nspacl --------------------------------------------------------------------- public | {postgres=UC/postgres,=UC/postgres} (1 row) \c - - - :master_port DROP TABLE public_schema_table; DROP ROLE role_1, role_2, role_3;