Change how permissions work with continuous aggs

To create a continuous agg you now only need SELECT and
TRIGGER permission on the raw table. To continue refreshing
the continuous agg the owner of the continuous agg needs
only SELECT permission.

This commit adds tests to make sure that removing the
SELECT permission removes ability to refresh using
both REFRESH MATERIALIZED VIEW and also through a background
worker.

This work also uncovered divergence in permission logic for
creating triggers by a CREATE TRIGGER on chunks and when new
chunks are created. This has now been unified: there is a check
to make sure you can create the trigger on the main table and
then there is a check that the owner of the main table can create
triggers on chunks.

Alter view for continuous aggregates is allowed for the owner of the
view.
This commit is contained in:
Matvey Arye 2019-06-14 15:18:49 -04:00 committed by Matvey Arye
parent d3e582fd23
commit d580abf04f
14 changed files with 744 additions and 84 deletions

View File

@ -433,6 +433,10 @@ ts_hypertable_create_trigger(Hypertable *ht, CreateTrigStmt *stmt, const char *q
ObjectAddress root_trigger_addr;
List *chunks;
ListCell *lc;
int sec_ctx;
Oid saved_uid;
Oid owner;
Assert(ht != NULL);
#if !PG96
if (stmt->transitionRels != NIL)
@ -451,6 +455,13 @@ ts_hypertable_create_trigger(Hypertable *ht, CreateTrigStmt *stmt, const char *q
if (!stmt->row)
return root_trigger_addr;
/* switch to the hypertable owner's role -- note that this logic must be the same as
* `ts_trigger_create_all_on_chunk` */
owner = ts_rel_get_owner(ht->main_table_relid);
GetUserIdAndSecContext(&saved_uid, &sec_ctx);
if (saved_uid != owner)
SetUserIdAndSecContext(owner, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);
chunks = find_inheritance_children(ht->main_table_relid, NoLock);
foreach (lc, chunks)
{
@ -461,6 +472,9 @@ ts_hypertable_create_trigger(Hypertable *ht, CreateTrigStmt *stmt, const char *q
ts_trigger_create_on_chunk(root_trigger_addr.objectId, relschema, relname);
}
if (saved_uid != owner)
SetUserIdAndSecContext(saved_uid, sec_ctx);
return root_trigger_addr;
}

View File

@ -2251,12 +2251,23 @@ process_altertable_start_table(ProcessUtilityArgs *args)
}
static void
process_altercontinuousagg_set_with(ContinuousAgg *cagg, const List *defelems)
continuous_agg_with_clause_perm_check(ContinuousAgg *cagg, Oid view_relid)
{
Oid ownerid = ts_rel_get_owner(view_relid);
if (!has_privs_of_role(GetUserId(), ownerid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of continuous aggregate \"%s\"", get_rel_name(view_relid))));
}
static void
process_altercontinuousagg_set_with(ContinuousAgg *cagg, Oid view_relid, const List *defelems)
{
WithClauseResult *parse_results;
List *pg_options = NIL, *cagg_options = NIL;
ts_hypertable_permissions_check_by_id(cagg->data.raw_hypertable_id);
continuous_agg_with_clause_perm_check(cagg, view_relid);
ts_with_clause_filter(defelems, &cagg_options, &pg_options);
if (list_length(pg_options) > 0)
@ -2293,7 +2304,7 @@ process_altertable_start_view(ProcessUtilityArgs *args)
if (cagg == NULL)
return false;
ts_hypertable_permissions_check_by_id(cagg->data.raw_hypertable_id);
continuous_agg_with_clause_perm_check(cagg, view_relid);
vtyp = ts_continuous_agg_view_type(&cagg->data, NameStr(view_schema), NameStr(view_name));
if (vtyp == ContinuousAggPartialView || vtyp == ContinuousAggDirectView)
@ -2312,7 +2323,7 @@ process_altertable_start_view(ProcessUtilityArgs *args)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("expected set options to contain a list")));
process_altercontinuousagg_set_with(cagg, (List *) cmd->def);
process_altercontinuousagg_set_with(cagg, view_relid, (List *) cmd->def);
break;
default:
ereport(ERROR,
@ -2329,7 +2340,6 @@ static bool
process_altertable_start(ProcessUtilityArgs *args)
{
AlterTableStmt *stmt = (AlterTableStmt *) args->parsetree;
switch (stmt->relkind)
{
case OBJECT_TABLE:

View File

@ -133,27 +133,17 @@ ts_trigger_create_all_on_chunk(Hypertable *ht, Chunk *chunk)
{
int sec_ctx;
Oid saved_uid;
HeapTuple tuple;
Form_pg_class form;
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(ht->main_table_relid));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for relation ID %u", ht->main_table_relid);
form = (Form_pg_class) GETSTRUCT(tuple);
Oid owner = ts_rel_get_owner(ht->main_table_relid);
GetUserIdAndSecContext(&saved_uid, &sec_ctx);
if (saved_uid != form->relowner)
SetUserIdAndSecContext(form->relowner, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);
if (saved_uid != owner)
SetUserIdAndSecContext(owner, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);
for_each_trigger(ht->main_table_relid, create_trigger_handler, chunk);
if (saved_uid != form->relowner)
if (saved_uid != owner)
SetUserIdAndSecContext(saved_uid, sec_ctx);
ReleaseSysCache(tuple);
}
#if !PG96

View File

@ -794,8 +794,6 @@ cagg_validate_query(Query *query)
errmsg("can create continuous aggregate only on hypertables")));
}
ts_hypertable_permissions_check_by_id(ht->fd.id);
/*check row security settings for the table */
if (has_row_security(rte->relid))
{

View File

@ -1185,37 +1185,3 @@ SELECT * FROM mat_refresh_test order by 1,2 ;
POR | 75
(4 rows)
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
table_name
---------------------------
conditions_for_perm_check
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
WARNING: Timescale License expired
ERROR: permission denied for hypertable "conditions"
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
ERROR: must be owner of view mat_refresh_test
DROP VIEW mat_refresh_test CASCADE;
ERROR: must be owner of view mat_refresh_test
REFRESH MATERIALIZED VIEW mat_refresh_test;
ERROR: permission denied for table conditions
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;
ERROR: permission denied for hypertable "conditions_for_perm_check"

View File

@ -320,3 +320,77 @@ invalidation_threshold | 6
job_status | scheduled
last_run_duration |
\x
--
-- Test creating continuous aggregate with a user that is the non-owner of the raw table
--
CREATE TABLE test_continuous_agg_table_w_grant(time int, data int);
SELECT create_hypertable('test_continuous_agg_table_w_grant', 'time', chunk_time_interval => 10);
NOTICE: adding not-null constraint to column "time"
create_hypertable
------------------------------------------------
(4,public,test_continuous_agg_table_w_grant,t)
(1 row)
GRANT SELECT, TRIGGER ON test_continuous_agg_table_w_grant TO public;
INSERT INTO test_continuous_agg_table_w_grant
SELECT i, i FROM
(SELECT generate_series(0, 10) as i) AS j;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
-- make sure view can be created
CREATE VIEW test_continuous_agg_view_user_2
WITH ( timescaledb.continuous,
timescaledb.max_interval_per_job='2',
timescaledb.refresh_lag='-2')
AS SELECT time_bucket('2', time), SUM(data) as value
FROM test_continuous_agg_table_w_grant
GROUP BY 1;
SELECT job_id FROM _timescaledb_catalog.continuous_agg ORDER BY job_id desc limit 1 \gset
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish
------------------------------------------------------------
(1 row)
SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
FROM _timescaledb_internal.bgw_job_stat
where job_id=:job_id;
job_id | next_start | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes
--------+----------------------------------+----------------------------------+------------------+------------+-----------------+----------------+---------------
1002 | Fri Dec 31 16:00:00.075 1999 PST | Fri Dec 31 16:00:00.075 1999 PST | t | 1 | 1 | 0 | 0
(1 row)
--view is populated
SELECT * FROM test_continuous_agg_view_user_2;
time_bucket | value
-------------+-------
0 | 1
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke permissions from the continuous agg view owner to select from raw table
--no further updates to cont agg should happen
REVOKE SELECT ON test_continuous_agg_table_w_grant FROM public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25, 25);
ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish
------------------------------------------------------------
(1 row)
--should show a failing execution because no longer has permissions (due to lack of permission on partial view owner's part)
SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
FROM _timescaledb_internal.bgw_job_stat
where job_id=:job_id;
job_id | next_start | until_next | last_run_success | total_runs | total_successes | total_failures | total_crashes
--------+--------------------------------+--------------------------------+------------------+------------+-----------------+----------------+---------------
1002 | Sat Jan 01 04:00:00.1 2000 PST | Fri Dec 31 16:00:00.1 1999 PST | f | 2 | 1 | 1 | 0
(1 row)
--view was NOT updated; but the old stuff is still there
SELECT * FROM test_continuous_agg_view_user_2;
time_bucket | value
-------------+-------
0 | 1
(1 row)

View File

@ -0,0 +1,150 @@
-- 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.
-- initialize the bgw mock state to prevent the materialization workers from running
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_bgw_params_create() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set WAIT_ON_JOB 0
\set IMMEDIATELY_SET_UNTIL 1
\set WAIT_FOR_OTHER_TO_ADVANCE 2
-- stop the background workers from locking up the tables,
-- and remove any default jobs, e.g., telemetry so bgw_job isn't polluted
SELECT _timescaledb_internal.stop_background_workers();
stop_background_workers
-------------------------
t
(1 row)
DELETE FROM _timescaledb_config.bgw_job WHERE TRUE;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE conditions (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=> 100);
table_name
------------
conditions
(1 row)
create or replace view mat_refresh_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_2_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_2 USING BTREE(location, time_partition_col)
insert into conditions
select generate_series(0, 50, 10), 'NYC', 55, 75, 40, 70, NULL;
REFRESH MATERIALIZED VIEW mat_refresh_test;
INFO: new materialization range for public.conditions (time column timec) (200)
INFO: materializing continuous aggregate public.mat_refresh_test: new range up to 200
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job order by id desc limit 1 \gset
SELECT materialization_hypertable FROM timescaledb_information.continuous_aggregates WHERE view_name = 'mat_refresh_test'::regclass \gset
SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg WHERE user_view_name = 'mat_refresh_test' \gset
SELECT schema_name as mat_chunk_schema, table_name as mat_chunk_table
FROM _timescaledb_catalog.chunk
WHERE hypertable_id = :mat_hypertable_id
ORDER BY id desc
LIMIT 1 \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
table_name
---------------------------
conditions_for_perm_check
(1 row)
CREATE TABLE conditions_for_perm_check_w_grant (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check_w_grant', 'timec', chunk_time_interval=> 100);
table_name
-----------------------------------
conditions_for_perm_check_w_grant
(1 row)
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
insert into conditions_for_perm_check_w_grant
select generate_series(0, 30, 10), 'POR', 55, 75, 40, 70, NULL;
--need both select and trigger permissions to create a materialized view on top of it.
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
WARNING: Timescale License expired
ERROR: permission denied for hypertable "conditions"
--make sure that commands fail
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
ERROR: must be owner of relation mat_refresh_test
DROP VIEW mat_refresh_test CASCADE;
ERROR: must be owner of relation mat_refresh_test
REFRESH MATERIALIZED VIEW mat_refresh_test;
ERROR: permission denied for relation conditions
SELECT * FROM mat_refresh_test;
ERROR: permission denied for relation mat_refresh_test
SELECT * FROM :materialization_hypertable;
ERROR: permission denied for relation _materialized_hypertable_2
SELECT * FROM :"mat_chunk_schema".:"mat_chunk_table";
ERROR: permission denied for relation _hyper_2_2_chunk
--cannot create a mat view without select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_5_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_5 USING BTREE(location, time_partition_col)
ERROR: permission denied for relation conditions_for_perm_check
--can create a mat view on something with select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check_w_grant
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_6_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_6 USING BTREE(location, time_partition_col)
REFRESH MATERIALIZED VIEW mat_perm_view_test;
INFO: new materialization range for public.conditions_for_perm_check_w_grant (time column timec) (200)
INFO: materializing continuous aggregate public.mat_perm_view_test: new range up to 200
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke select permissions from role with mat view
REVOKE SELECT ON conditions_for_perm_check_w_grant FROM public;
insert into conditions_for_perm_check_w_grant
select generate_series(100, 130, 10), 'POR', 65, 85, 30, 90, NULL;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--refresh mat view should now fail due to lack of permissions
REFRESH MATERIALIZED VIEW mat_perm_view_test;
ERROR: permission denied for relation conditions_for_perm_check_w_grant
--but the old data will still be there
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)

View File

@ -0,0 +1,150 @@
-- 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.
-- initialize the bgw mock state to prevent the materialization workers from running
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_bgw_params_create() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set WAIT_ON_JOB 0
\set IMMEDIATELY_SET_UNTIL 1
\set WAIT_FOR_OTHER_TO_ADVANCE 2
-- stop the background workers from locking up the tables,
-- and remove any default jobs, e.g., telemetry so bgw_job isn't polluted
SELECT _timescaledb_internal.stop_background_workers();
stop_background_workers
-------------------------
t
(1 row)
DELETE FROM _timescaledb_config.bgw_job WHERE TRUE;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE conditions (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=> 100);
table_name
------------
conditions
(1 row)
create or replace view mat_refresh_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_2_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_2 USING BTREE(location, time_partition_col)
insert into conditions
select generate_series(0, 50, 10), 'NYC', 55, 75, 40, 70, NULL;
REFRESH MATERIALIZED VIEW mat_refresh_test;
INFO: new materialization range for public.conditions (time column timec) (200)
INFO: materializing continuous aggregate public.mat_refresh_test: new range up to 200
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job order by id desc limit 1 \gset
SELECT materialization_hypertable FROM timescaledb_information.continuous_aggregates WHERE view_name = 'mat_refresh_test'::regclass \gset
SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg WHERE user_view_name = 'mat_refresh_test' \gset
SELECT schema_name as mat_chunk_schema, table_name as mat_chunk_table
FROM _timescaledb_catalog.chunk
WHERE hypertable_id = :mat_hypertable_id
ORDER BY id desc
LIMIT 1 \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
table_name
---------------------------
conditions_for_perm_check
(1 row)
CREATE TABLE conditions_for_perm_check_w_grant (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check_w_grant', 'timec', chunk_time_interval=> 100);
table_name
-----------------------------------
conditions_for_perm_check_w_grant
(1 row)
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
insert into conditions_for_perm_check_w_grant
select generate_series(0, 30, 10), 'POR', 55, 75, 40, 70, NULL;
--need both select and trigger permissions to create a materialized view on top of it.
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
WARNING: Timescale License expired
ERROR: permission denied for hypertable "conditions"
--make sure that commands fail
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
ERROR: must be owner of view mat_refresh_test
DROP VIEW mat_refresh_test CASCADE;
ERROR: must be owner of view mat_refresh_test
REFRESH MATERIALIZED VIEW mat_refresh_test;
ERROR: permission denied for table conditions
SELECT * FROM mat_refresh_test;
ERROR: permission denied for view mat_refresh_test
SELECT * FROM :materialization_hypertable;
ERROR: permission denied for table _materialized_hypertable_2
SELECT * FROM :"mat_chunk_schema".:"mat_chunk_table";
ERROR: permission denied for table _hyper_2_2_chunk
--cannot create a mat view without select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_5_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_5 USING BTREE(location, time_partition_col)
ERROR: permission denied for table conditions_for_perm_check
--can create a mat view on something with select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check_w_grant
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_6_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_6 USING BTREE(location, time_partition_col)
REFRESH MATERIALIZED VIEW mat_perm_view_test;
INFO: new materialization range for public.conditions_for_perm_check_w_grant (time column timec) (200)
INFO: materializing continuous aggregate public.mat_perm_view_test: new range up to 200
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke select permissions from role with mat view
REVOKE SELECT ON conditions_for_perm_check_w_grant FROM public;
insert into conditions_for_perm_check_w_grant
select generate_series(100, 130, 10), 'POR', 65, 85, 30, 90, NULL;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--refresh mat view should now fail due to lack of permissions
REFRESH MATERIALIZED VIEW mat_perm_view_test;
ERROR: permission denied for table conditions_for_perm_check_w_grant
--but the old data will still be there
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)

View File

@ -0,0 +1,150 @@
-- 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.
-- initialize the bgw mock state to prevent the materialization workers from running
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_bgw_params_create() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set WAIT_ON_JOB 0
\set IMMEDIATELY_SET_UNTIL 1
\set WAIT_FOR_OTHER_TO_ADVANCE 2
-- stop the background workers from locking up the tables,
-- and remove any default jobs, e.g., telemetry so bgw_job isn't polluted
SELECT _timescaledb_internal.stop_background_workers();
stop_background_workers
-------------------------
t
(1 row)
DELETE FROM _timescaledb_config.bgw_job WHERE TRUE;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE conditions (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=> 100);
table_name
------------
conditions
(1 row)
create or replace view mat_refresh_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_2_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_2 USING BTREE(location, time_partition_col)
insert into conditions
select generate_series(0, 50, 10), 'NYC', 55, 75, 40, 70, NULL;
REFRESH MATERIALIZED VIEW mat_refresh_test;
INFO: new materialization range for public.conditions (time column timec) (200)
INFO: materializing continuous aggregate public.mat_refresh_test: new range up to 200
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job order by id desc limit 1 \gset
SELECT materialization_hypertable FROM timescaledb_information.continuous_aggregates WHERE view_name = 'mat_refresh_test'::regclass \gset
SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg WHERE user_view_name = 'mat_refresh_test' \gset
SELECT schema_name as mat_chunk_schema, table_name as mat_chunk_table
FROM _timescaledb_catalog.chunk
WHERE hypertable_id = :mat_hypertable_id
ORDER BY id desc
LIMIT 1 \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
table_name
---------------------------
conditions_for_perm_check
(1 row)
CREATE TABLE conditions_for_perm_check_w_grant (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check_w_grant', 'timec', chunk_time_interval=> 100);
table_name
-----------------------------------
conditions_for_perm_check_w_grant
(1 row)
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
insert into conditions_for_perm_check_w_grant
select generate_series(0, 30, 10), 'POR', 55, 75, 40, 70, NULL;
--need both select and trigger permissions to create a materialized view on top of it.
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
WARNING: Timescale License expired
ERROR: permission denied for hypertable "conditions"
--make sure that commands fail
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
ERROR: must be owner of relation mat_refresh_test
DROP VIEW mat_refresh_test CASCADE;
ERROR: must be owner of relation mat_refresh_test
REFRESH MATERIALIZED VIEW mat_refresh_test;
ERROR: permission denied for relation conditions
SELECT * FROM mat_refresh_test;
ERROR: permission denied for relation mat_refresh_test
SELECT * FROM :materialization_hypertable;
ERROR: permission denied for relation _materialized_hypertable_2
SELECT * FROM :"mat_chunk_schema".:"mat_chunk_table";
ERROR: permission denied for relation _hyper_2_2_chunk
--cannot create a mat view without select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_5_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_5 USING BTREE(location, time_partition_col)
ERROR: permission denied for relation conditions_for_perm_check
--can create a mat view on something with select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check_w_grant
group by time_bucket(100, timec), location;
NOTICE: adding index _materialized_hypertable_6_location_time_partition_col_idx ON _timescaledb_internal._materialized_hypertable_6 USING BTREE(location, time_partition_col)
REFRESH MATERIALIZED VIEW mat_perm_view_test;
INFO: new materialization range for public.conditions_for_perm_check_w_grant (time column timec) (200)
INFO: materializing continuous aggregate public.mat_perm_view_test: new range up to 200
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke select permissions from role with mat view
REVOKE SELECT ON conditions_for_perm_check_w_grant FROM public;
insert into conditions_for_perm_check_w_grant
select generate_series(100, 130, 10), 'POR', 65, 85, 30, 90, NULL;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--refresh mat view should now fail due to lack of permissions
REFRESH MATERIALIZED VIEW mat_perm_view_test;
ERROR: permission denied for relation conditions_for_perm_check_w_grant
--but the old data will still be there
SELECT * FROM mat_perm_view_test;
location | max
----------+-----
POR | 75
(1 row)

View File

@ -1,2 +1,3 @@
/plan_gapfill-*.sql
/continuous_aggs_ddl-*.sql
/continuous_aggs_permissions-*.sql

View File

@ -23,6 +23,7 @@ set(TEST_TEMPLATES
# parallel plans are different between postgres versions
plan_gapfill.sql.in
continuous_aggs_ddl.sql.in
continuous_aggs_permissions.sql.in
)
if (CMAKE_BUILD_TYPE MATCHES Debug)

View File

@ -857,32 +857,3 @@ select generate_series(0, 50, 10), 'NYC', 55, 75, 40, 70, NULL;
REFRESH MATERIALIZED VIEW mat_refresh_test;
SELECT * FROM mat_refresh_test order by 1,2 ;
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
DROP VIEW mat_refresh_test CASCADE;
REFRESH MATERIALIZED VIEW mat_refresh_test;
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;

View File

@ -174,3 +174,54 @@ select view_name, view_definition from timescaledb_information.continuous_aggreg
where view_name::text like '%test_continuous_agg_view';
select view_name, completed_threshold, invalidation_threshold, job_status, last_run_duration from timescaledb_information.continuous_aggregate_stats where view_name::text like '%test_continuous_agg_view';
\x
--
-- Test creating continuous aggregate with a user that is the non-owner of the raw table
--
CREATE TABLE test_continuous_agg_table_w_grant(time int, data int);
SELECT create_hypertable('test_continuous_agg_table_w_grant', 'time', chunk_time_interval => 10);
GRANT SELECT, TRIGGER ON test_continuous_agg_table_w_grant TO public;
INSERT INTO test_continuous_agg_table_w_grant
SELECT i, i FROM
(SELECT generate_series(0, 10) as i) AS j;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
-- make sure view can be created
CREATE VIEW test_continuous_agg_view_user_2
WITH ( timescaledb.continuous,
timescaledb.max_interval_per_job='2',
timescaledb.refresh_lag='-2')
AS SELECT time_bucket('2', time), SUM(data) as value
FROM test_continuous_agg_table_w_grant
GROUP BY 1;
SELECT job_id FROM _timescaledb_catalog.continuous_agg ORDER BY job_id desc limit 1 \gset
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25);
SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
FROM _timescaledb_internal.bgw_job_stat
where job_id=:job_id;
--view is populated
SELECT * FROM test_continuous_agg_view_user_2;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke permissions from the continuous agg view owner to select from raw table
--no further updates to cont agg should happen
REVOKE SELECT ON test_continuous_agg_table_w_grant FROM public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
SELECT ts_bgw_db_scheduler_test_run_and_wait_for_scheduler_finish(25, 25);
--should show a failing execution because no longer has permissions (due to lack of permission on partial view owner's part)
SELECT job_id, next_start, last_finish as until_next, last_run_success, total_runs, total_successes, total_failures, total_crashes
FROM _timescaledb_internal.bgw_job_stat
where job_id=:job_id;
--view was NOT updated; but the old stuff is still there
SELECT * FROM test_continuous_agg_view_user_2;

View File

@ -0,0 +1,134 @@
-- 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.
-- initialize the bgw mock state to prevent the materialization workers from running
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE OR REPLACE FUNCTION ts_bgw_params_create() RETURNS VOID
AS :MODULE_PATHNAME LANGUAGE C VOLATILE;
\set WAIT_ON_JOB 0
\set IMMEDIATELY_SET_UNTIL 1
\set WAIT_FOR_OTHER_TO_ADVANCE 2
-- stop the background workers from locking up the tables,
-- and remove any default jobs, e.g., telemetry so bgw_job isn't polluted
SELECT _timescaledb_internal.stop_background_workers();
DELETE FROM _timescaledb_config.bgw_job WHERE TRUE;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
CREATE TABLE conditions (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable( 'conditions', 'timec', chunk_time_interval=> 100);
create or replace view mat_refresh_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions
group by time_bucket(100, timec), location;
insert into conditions
select generate_series(0, 50, 10), 'NYC', 55, 75, 40, 70, NULL;
REFRESH MATERIALIZED VIEW mat_refresh_test;
SELECT id as cagg_job_id FROM _timescaledb_config.bgw_job order by id desc limit 1 \gset
SELECT materialization_hypertable FROM timescaledb_information.continuous_aggregates WHERE view_name = 'mat_refresh_test'::regclass \gset
SELECT mat_hypertable_id FROM _timescaledb_catalog.continuous_agg WHERE user_view_name = 'mat_refresh_test' \gset
SELECT schema_name as mat_chunk_schema, table_name as mat_chunk_table
FROM _timescaledb_catalog.chunk
WHERE hypertable_id = :mat_hypertable_id
ORDER BY id desc
LIMIT 1 \gset
CREATE TABLE conditions_for_perm_check (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check', 'timec', chunk_time_interval=> 100);
CREATE TABLE conditions_for_perm_check_w_grant (
timec INT NOT NULL,
location TEXT NOT NULL,
temperature DOUBLE PRECISION NULL,
humidity DOUBLE PRECISION NULL,
lowp double precision NULL,
highp double precision null,
allnull double precision null
);
select table_name from create_hypertable('conditions_for_perm_check_w_grant', 'timec', chunk_time_interval=> 100);
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
insert into conditions_for_perm_check_w_grant
select generate_series(0, 30, 10), 'POR', 55, 75, 40, 70, NULL;
--need both select and trigger permissions to create a materialized view on top of it.
GRANT SELECT, TRIGGER ON conditions_for_perm_check_w_grant TO public;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
\set ON_ERROR_STOP 0
select from alter_job_schedule(:cagg_job_id, max_runtime => NULL);
--make sure that commands fail
ALTER VIEW mat_refresh_test SET(timescaledb.refresh_lag = '6 h', timescaledb.refresh_interval = '2h');
DROP VIEW mat_refresh_test CASCADE;
REFRESH MATERIALIZED VIEW mat_refresh_test;
SELECT * FROM mat_refresh_test;
SELECT * FROM :materialization_hypertable;
SELECT * FROM :"mat_chunk_schema".:"mat_chunk_table";
--cannot create a mat view without select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check
group by time_bucket(100, timec), location;
--can create a mat view on something with select and trigger grants
create or replace view mat_perm_view_test
WITH ( timescaledb.continuous, timescaledb.refresh_lag = '-200')
as
select location, max(humidity)
from conditions_for_perm_check_w_grant
group by time_bucket(100, timec), location;
REFRESH MATERIALIZED VIEW mat_perm_view_test;
SELECT * FROM mat_perm_view_test;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
--revoke select permissions from role with mat view
REVOKE SELECT ON conditions_for_perm_check_w_grant FROM public;
insert into conditions_for_perm_check_w_grant
select generate_series(100, 130, 10), 'POR', 65, 85, 30, 90, NULL;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
--refresh mat view should now fail due to lack of permissions
REFRESH MATERIALIZED VIEW mat_perm_view_test;
--but the old data will still be there
SELECT * FROM mat_perm_view_test;