mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-17 11:03:36 +08:00
Fix chunk creation with user that lacks TRIGGER permission
Previously, automatic chunk creation on INSERT failed due to lack of permissions when the hypertable had triggers that needed to be replicated to the new chunk. This happened because creating triggers on tables requires TRIGGER permission, and the internal code used CreateTrigger() that performs permissions checks. Thus, if the user inserting tuples into the hypertable only had INSERT permissions, the insert would fail whenever a new chunk was created. This change fixes the issue by temporarily assuming the role of the hypertable owner when executing CreateTrigger() on the new chunk.
This commit is contained in:
parent
43f812c158
commit
fe20e489a9
@ -1,10 +1,12 @@
|
||||
#include <postgres.h>
|
||||
#include <utils/rel.h>
|
||||
#include <utils/lsyscache.h>
|
||||
#include <utils/syscache.h>
|
||||
#include <utils/builtins.h>
|
||||
#include <tcop/tcopprot.h>
|
||||
#include <commands/trigger.h>
|
||||
#include <access/xact.h>
|
||||
#include <miscadmin.h>
|
||||
|
||||
#include "trigger.h"
|
||||
#include "compat.h"
|
||||
@ -56,8 +58,16 @@ trigger_by_name(Oid relid, const char *trigname, bool missing_ok)
|
||||
return trigger;
|
||||
}
|
||||
|
||||
/* all creation of triggers on chunks should go through this. Strictly speaking,
|
||||
* this deparsing is not necessary in all cases, but this keeps things consistent. */
|
||||
/*
|
||||
* Replicate a trigger on a chunk.
|
||||
*
|
||||
* Given a trigger OID (e.g., a Hypertable trigger), create the equivalent
|
||||
* trigger on a chunk.
|
||||
*
|
||||
* Note: it is assumed that this function is called under a user that has
|
||||
* permissions to modify the chunk since CreateTrigger() performs permissions
|
||||
* checks.
|
||||
*/
|
||||
void
|
||||
trigger_create_on_chunk(Oid trigger_oid, char *chunk_schema_name, char *chunk_table_name)
|
||||
{
|
||||
@ -118,7 +128,7 @@ for_each_trigger(Oid relid, trigger_handler on_trigger, void *arg)
|
||||
Trigger *trigger = &trigdesc->triggers[i];
|
||||
|
||||
if (!on_trigger(trigger, arg))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,10 +155,44 @@ create_trigger_handler(Trigger *trigger, void *arg)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create all hypertable triggers on a new chunk.
|
||||
*
|
||||
* Since chunk creation typically happens automatically on hypertable INSERT, we
|
||||
* need to execute the trigger creation under the role of the hypertable owner.
|
||||
* This is due to the use of CreateTrigger(), which does permissions checks. The
|
||||
* user role inserting might have INSERT permissions, but not TRIGGER
|
||||
* permissions (needed to create triggers on a table).
|
||||
*
|
||||
* We assume that the owner of the Hypertable is also the owner of the new
|
||||
* chunk.
|
||||
*/
|
||||
void
|
||||
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 %u", ht->main_table_relid);
|
||||
|
||||
form = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
GetUserIdAndSecContext(&saved_uid, &sec_ctx);
|
||||
|
||||
if (saved_uid != form->relowner)
|
||||
SetUserIdAndSecContext(form->relowner, sec_ctx | SECURITY_LOCAL_USERID_CHANGE);
|
||||
|
||||
for_each_trigger(ht->main_table_relid, create_trigger_handler, chunk);
|
||||
|
||||
if (saved_uid != form->relowner)
|
||||
SetUserIdAndSecContext(saved_uid, sec_ctx);
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
|
||||
#if PG10
|
||||
|
@ -285,7 +285,7 @@ CREATE TABLE location (
|
||||
CREATE OR REPLACE FUNCTION create_vehicle_trigger_fn()
|
||||
RETURNS TRIGGER LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
BEGIN
|
||||
INSERT INTO vehicles VALUES(NEW.vehicle_id, NULL, NULL) ON CONFLICT DO NOTHING;
|
||||
RETURN NEW;
|
||||
END
|
||||
@ -298,14 +298,11 @@ BEGIN
|
||||
BEGIN
|
||||
INSERT INTO color VALUES(NEW.color_id, 'n/a');
|
||||
EXCEPTION WHEN unique_violation THEN
|
||||
-- Nothing to do, just continue
|
||||
END;
|
||||
-- Nothing to do, just continue
|
||||
END;
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$;
|
||||
CREATE TRIGGER create_vehicle_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_vehicle_trigger_fn();
|
||||
CREATE TRIGGER create_color_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_color_trigger_fn();
|
||||
@ -322,6 +319,14 @@ SELECT create_hypertable('color', 'color_id', chunk_time_interval=>10);
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Test that we can create and use triggers with another user
|
||||
GRANT TRIGGER, INSERT, SELECT, UPDATE ON location TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
GRANT SELECT, INSERT, UPDATE ON color TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
GRANT SELECT, INSERT, UPDATE ON vehicles TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
\c single :ROLE_DEFAULT_PERM_USER_2;
|
||||
CREATE TRIGGER create_vehicle_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_vehicle_trigger_fn();
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:03', 1, 1, 40.7493226,-73.9771259);
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:04', 1, 20, 24.7493226,-73.9771259);
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:03', 23, 1, 40.7493226,-73.9771269);
|
||||
|
@ -222,7 +222,7 @@ CREATE TABLE location (
|
||||
CREATE OR REPLACE FUNCTION create_vehicle_trigger_fn()
|
||||
RETURNS TRIGGER LANGUAGE PLPGSQL AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
BEGIN
|
||||
INSERT INTO vehicles VALUES(NEW.vehicle_id, NULL, NULL) ON CONFLICT DO NOTHING;
|
||||
RETURN NEW;
|
||||
END
|
||||
@ -237,25 +237,31 @@ BEGIN
|
||||
BEGIN
|
||||
INSERT INTO color VALUES(NEW.color_id, 'n/a');
|
||||
EXCEPTION WHEN unique_violation THEN
|
||||
-- Nothing to do, just continue
|
||||
END;
|
||||
-- Nothing to do, just continue
|
||||
END;
|
||||
RETURN NEW;
|
||||
END
|
||||
$BODY$;
|
||||
|
||||
CREATE TRIGGER create_vehicle_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_vehicle_trigger_fn();
|
||||
|
||||
CREATE TRIGGER create_color_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_color_trigger_fn();
|
||||
|
||||
|
||||
SELECT create_hypertable('location', 'time');
|
||||
|
||||
--make color also a hypertable
|
||||
SELECT create_hypertable('color', 'color_id', chunk_time_interval=>10);
|
||||
|
||||
-- Test that we can create and use triggers with another user
|
||||
GRANT TRIGGER, INSERT, SELECT, UPDATE ON location TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
GRANT SELECT, INSERT, UPDATE ON color TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
GRANT SELECT, INSERT, UPDATE ON vehicles TO :ROLE_DEFAULT_PERM_USER_2;
|
||||
\c single :ROLE_DEFAULT_PERM_USER_2;
|
||||
|
||||
CREATE TRIGGER create_vehicle_trigger
|
||||
BEFORE INSERT OR UPDATE ON location
|
||||
FOR EACH ROW EXECUTE PROCEDURE create_vehicle_trigger_fn();
|
||||
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:03', 1, 1, 40.7493226,-73.9771259);
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:04', 1, 20, 24.7493226,-73.9771259);
|
||||
INSERT INTO location VALUES('2017-01-01 01:02:03', 23, 1, 40.7493226,-73.9771269);
|
||||
|
Loading…
x
Reference in New Issue
Block a user