timescaledb/tsl/test/expected/compression_permissions.out
Mats Kindahl b788168e59 Propagate grants to compressed hypertable
Grants and revokes where not propagated to compressed hypertables, if
the hypertable had a compressed hypertable, meaning that `pg_dump` and
`pg_restore` would not be able to dump nor restore the compressed part
of a hypertable unless they were owners and/or was a superuser.

This commit fixes this by propagating grants and revokes on a
hypertable to the associated compressed hypertable, if one exists,
which will then include the compressed hypertable and the associated
chunks in the grant and revoke execution.

It also adds code to fix the permissions of compressed hypertables and
all associated chunks in an update and adds an update test to check
that the permissions match.

Fixes #3209
2021-05-13 08:43:07 +02:00

351 lines
16 KiB
Plaintext

-- This file and its contents are licensed under the Timescale License.
-- Please see the included NOTICE for copyright information and
-- LICENSE-TIMESCALE for a copy of the license.
\set ON_ERROR_STOP 0
CREATE VIEW hypertable_details AS
WITH
names AS (SELECT * FROM pg_class cl JOIN pg_namespace ns ON ns.oid = relnamespace)
SELECT (SELECT format('%I.%I', ht.schema_name, ht.table_name) FROM names
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable,
(SELECT relacl FROM names
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable_acl,
(SELECT format('%I.%I', ct.schema_name, ct.table_name) FROM names
WHERE ct.schema_name = nspname AND ct.table_name = relname) AS compressed,
(SELECT relacl FROM names
WHERE ct.schema_name = nspname AND ct.table_name = relname) AS compressed_acl
FROM _timescaledb_catalog.hypertable ht
LEFT JOIN _timescaledb_catalog.hypertable ct ON ht.compressed_hypertable_id = ct.id;
CREATE VIEW chunk_details AS
WITH
names AS (SELECT * FROM pg_class cl JOIN pg_namespace ns ON ns.oid = relnamespace)
SELECT (SELECT format('%I.%I', ht.schema_name, ht.table_name) FROM names
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable,
(SELECT relacl FROM names
WHERE ht.schema_name = nspname AND ht.table_name = relname) AS hypertable_acl,
(SELECT format('%I.%I', ch.schema_name, ch.table_name) FROM names
WHERE ch.schema_name = nspname AND ch.table_name = relname) AS chunk,
(SELECT relacl FROM names
WHERE ch.schema_name = nspname AND ch.table_name = relname) AS chunk_acl
FROM _timescaledb_catalog.hypertable ht
JOIN _timescaledb_catalog.chunk ch ON ch.hypertable_id = ht.id;
CREATE TABLE conditions (
timec TIMESTAMPTZ NOT NULL,
location TEXT NOT NULL,
location2 char(10) NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL
);
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=>'1month'::interval);
table_name
------------
conditions
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'timec');
ERROR: must be owner of table conditions
insert into conditions
select generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 'klick', 55, 75;
ERROR: permission denied for table conditions
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--now owner tries and succeeds --
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'timec');
insert into conditions
select generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 'klick', 55, 75;
--try modifying compress properties --
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
alter table conditions set (timescaledb.compress, timescaledb.compress_segmentby = 'location', timescaledb.compress_orderby = 'humidity');
ERROR: must be owner of table conditions
--- compress_chunks and decompress_chunks fail without correct perm --
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
select compress_chunk(ch1.schema_name|| '.' || ch1.table_name)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' and ch1.compressed_chunk_id IS NULL;
ERROR: must be owner of hypertable "conditions"
select decompress_chunk(ch1.schema_name|| '.' || ch1.table_name)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions';
ERROR: must be owner of hypertable "conditions"
select add_compression_policy('conditions', '1day'::interval);
ERROR: must be owner of hypertable "conditions"
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
select add_compression_policy('conditions', '1day'::interval);
add_compression_policy
------------------------
1000
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--try dropping policy
select remove_compression_policy('conditions', true);
ERROR: must be owner of hypertable "conditions"
--Tests for GRANTS.
-- as owner grant select , compress chunk and check SELECT works
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
GRANT SELECT on conditions to :ROLE_DEFAULT_PERM_USER_2;
select count(*) from
(select compress_chunk(ch1.schema_name|| '.' || ch1.table_name)
FROM _timescaledb_catalog.chunk ch1, _timescaledb_catalog.hypertable ht where ch1.hypertable_id = ht.id and ht.table_name like 'conditions' and ch1.compressed_chunk_id IS NULL ) as subq;
count
-------
2
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
select count(*) from conditions;
count
-------
31
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
GRANT INSERT on conditions to :ROLE_DEFAULT_PERM_USER_2;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
insert into conditions
select '2019-04-01 00:00+0'::timestamp with time zone, 'NYC', 'klick', 55, 75;
select count(*) from conditions;
count
-------
32
(1 row)
update conditions
set location = 'SFO'
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
ERROR: permission denied for table conditions
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
GRANT UPDATE, DELETE on conditions to :ROLE_DEFAULT_PERM_USER_2;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
select location from conditions where timec = '2019-04-01 00:00+0';
location
----------
NYC
(1 row)
--NOTE constraint exclusion excludes the other chunks here
--otherwise update would fail
update conditions
set location = 'SFO'
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
select location from conditions where timec = '2019-04-01 00:00+0';
location
----------
SFO
(1 row)
--update expected to fail as executor touches all chunks
update conditions
set location = 'PNC'
where location = 'SFO';
ERROR: cannot update/delete rows from chunk "_hyper_1_1_chunk" as it is compressed
delete from conditions
where timec = '2019-04-01 00:00+0'::timestamp with time zone;
select location from conditions where timec = '2019-04-01 00:00+0';
location
----------
(0 rows)
CREATE VIEW v2 as select * from conditions;
select count(*) from v2;
count
-------
31
(1 row)
--should fail after revoking permissions
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
REVOKE SELECT on conditions FROM :ROLE_DEFAULT_PERM_USER_2;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
select count(*) from v2;
ERROR: permission denied for table conditions
\c :TEST_DBNAME :ROLE_SUPERUSER
DROP TABLE conditions CASCADE;
NOTICE: drop cascades to 2 other objects
NOTICE: drop cascades to view v2
-- Testing that permissions propagate to compressed hypertables and to
-- compressed chunks.
CREATE TABLE conditions (
timec TIMESTAMPTZ NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL
);
SELECT table_name FROM create_hypertable( 'conditions', 'timec',
chunk_time_interval => '1 week'::interval);
table_name
------------
conditions
(1 row)
ALTER TABLE conditions SET (
timescaledb.compress,
timescaledb.compress_segmentby = 'location',
timescaledb.compress_orderby = 'timec'
);
INSERT INTO conditions
SELECT generate_series('2018-12-01 00:00'::timestamp, '2018-12-31 00:00'::timestamp, '1 day'), 'POR', 55, 75;
SELECT compress_chunk(show_chunks('conditions'));
compress_chunk
-----------------------------------------
_timescaledb_internal._hyper_3_6_chunk
_timescaledb_internal._hyper_3_7_chunk
_timescaledb_internal._hyper_3_8_chunk
_timescaledb_internal._hyper_3_9_chunk
_timescaledb_internal._hyper_3_10_chunk
(5 rows)
-- Check that ACL propagates to compressed hypertable. We could prune
-- the listing by only selecting chunks where the ACL does not match
-- the hypertable ACL, but for now we list all to make debugging easy.
\x on
SELECT * FROM hypertable_details WHERE hypertable = 'public.conditions';
-[ RECORD 1 ]--+-----------------------------------------------
hypertable | public.conditions
hypertable_acl |
compressed | _timescaledb_internal._compressed_hypertable_4
compressed_acl |
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
ORDER BY hypertable, chunk;
-[ RECORD 1 ]--+------------------------------------------------
hypertable | public.conditions
hypertable_acl |
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
chunk_acl |
-[ RECORD 2 ]--+------------------------------------------------
hypertable | public.conditions
hypertable_acl |
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
chunk_acl |
-[ RECORD 3 ]--+------------------------------------------------
hypertable | public.conditions
hypertable_acl |
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
chunk_acl |
-[ RECORD 4 ]--+------------------------------------------------
hypertable | public.conditions
hypertable_acl |
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
chunk_acl |
-[ RECORD 5 ]--+------------------------------------------------
hypertable | public.conditions
hypertable_acl |
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
chunk_acl |
GRANT SELECT ON conditions TO :ROLE_DEFAULT_PERM_USER;
SELECT * FROM hypertable_details WHERE hypertable = 'public.conditions';
-[ RECORD 1 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
compressed | _timescaledb_internal._compressed_hypertable_4
compressed_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
ORDER BY hypertable, chunk;
-[ RECORD 1 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 2 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 3 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 4 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 5 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-- Add some new data and compress the chunks. The chunks should get
-- the permissions of the hypertable. We pick a start date to make
-- sure that we are not inserting into an already compressed chunk.
INSERT INTO conditions
SELECT generate_series('2019-01-07 00:00'::timestamp, '2019-02-07 00:00'::timestamp, '1 day'), 'XYZ', 47, 11;
SELECT compress_chunk(show_chunks('conditions', newer_than => '2019-01-01'));
-[ RECORD 1 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_16_chunk
-[ RECORD 2 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_17_chunk
-[ RECORD 3 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_18_chunk
-[ RECORD 4 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_19_chunk
-[ RECORD 5 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_20_chunk
-[ RECORD 6 ]--+----------------------------------------
compress_chunk | _timescaledb_internal._hyper_3_21_chunk
SELECT htd.hypertable, htd.hypertable_acl, chunk, chunk_acl
FROM chunk_details chd JOIN hypertable_details htd ON chd.hypertable = htd.compressed
ORDER BY hypertable, chunk;
-[ RECORD 1 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_11_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 2 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_12_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 3 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_13_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 4 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_14_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 5 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_15_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 6 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_22_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 7 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_23_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 8 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_24_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 9 ]--+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_25_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 10 ]-+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_26_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
-[ RECORD 11 ]-+---------------------------------------------------------------
hypertable | public.conditions
hypertable_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
chunk | _timescaledb_internal.compress_hyper_4_27_chunk
chunk_acl | {super_user=arwdDxt/super_user,default_perm_user=r/super_user}
\x off