Add permission checks to run_job()

There were no permission checks when calling run_job(), so it was
possible to execute any job regardless of who owned it. This commit
adds such checks.
This commit is contained in:
Mats Kindahl 2023-04-25 18:01:48 +02:00 committed by Mats Kindahl
parent 4a6650d170
commit d3730a4f6a
7 changed files with 89 additions and 5 deletions

View File

@ -27,6 +27,7 @@ accidentally triggering the load of a previous DB version.**
* #5570 Improve interpolate error message on datatype mismatch
* #5583 Fix parameterization in DecompressChunk path generation
* #5602 Fix broken CAgg with JOIN repair function
* #5615 Add permission checks to run_job()
**Thanks**
* @kovetskiy and @DZDomi for reporting peformance regression in Realtime Continuous Aggregates

View File

@ -974,12 +974,21 @@ ts_bgw_job_check_max_retries(BgwJob *job)
}
void
ts_bgw_job_permission_check(BgwJob *job)
ts_bgw_job_permission_check(BgwJob *job, const char *cmd)
{
if (!has_privs_of_role(GetUserId(), job->fd.owner))
{
const char *owner_name = GetUserNameFromId(job->fd.owner, false);
const char *user_name = GetUserNameFromId(GetUserId(), false);
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient permissions to alter job %d", job->fd.id)));
errmsg("insufficient permissions to %s job %d", cmd, job->fd.id),
errdetail("Job %d is owned by role \"%s\" but user \"%s\" does not belong to that "
"role.",
job->fd.id,
owner_name,
user_name)));
}
}
void

View File

@ -47,7 +47,7 @@ extern TSDLLEXPORT int32 ts_bgw_job_insert_relation(
Interval *retry_period, Name proc_schema, Name proc_name, Name check_schema, Name check_name,
Oid owner, bool scheduled, bool fixed_schedule, int32 hypertable_id, Jsonb *config,
TimestampTz initial_start, const char *timezone);
extern TSDLLEXPORT void ts_bgw_job_permission_check(BgwJob *job);
extern TSDLLEXPORT void ts_bgw_job_permission_check(BgwJob *job, const char *cmd);
extern TSDLLEXPORT void ts_bgw_job_validate_job_owner(Oid owner);

View File

@ -263,6 +263,8 @@ job_run(PG_FUNCTION_ARGS)
int32 job_id = PG_GETARG_INT32(0);
BgwJob *job = find_job(job_id, PG_ARGISNULL(0), false);
ts_bgw_job_permission_check(job, "run");
job_execute(job);
PG_RETURN_VOID();
@ -326,7 +328,7 @@ job_alter(PG_FUNCTION_ARGS)
if (job == NULL)
PG_RETURN_NULL();
ts_bgw_job_permission_check(job);
ts_bgw_job_permission_check(job, "alter");
if (!PG_ARGISNULL(1))
job->fd.schedule_interval = *PG_GETARG_INTERVAL_P(1);
@ -463,7 +465,8 @@ job_alter_set_hypertable_id(PG_FUNCTION_ARGS)
BgwJob *job = find_job(job_id, PG_ARGISNULL(0), false /* missing_ok */);
if (job == NULL)
PG_RETURN_NULL();
ts_bgw_job_permission_check(job);
ts_bgw_job_permission_check(job, "alter");
if (!PG_ARGISNULL(1))
{

View File

@ -0,0 +1,32 @@
-- 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 ROLE_ADMIN :TEST_DBNAME _admin
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE ROLE :ROLE_ADMIN;
GRANT :ROLE_ADMIN TO :ROLE_DEFAULT_PERM_USER;
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE TABLE custom_log (ts integer, msg text);
GRANT ALL ON custom_log TO PUBLIC;
CREATE PROCEDURE custom_job(integer, jsonb) AS $$
INSERT INTO custom_log values($1, 'custom_job');
$$ LANGUAGE SQL;
SELECT add_job('custom_job', '1h') AS job_id \gset
-- Set the owner of the job to the admin role
UPDATE _timescaledb_config.bgw_job SET owner = :'ROLE_ADMIN' WHERE id = :job_id;
SELECT id, proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
id | proc_name | owner
------+------------+-----------------------
1000 | custom_job | db_bgw_security_admin
(1 row)
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
-- We should fail to execute the job since we do not own it or belong
-- to the group that owns it.
\set ON_ERROR_STOP 0
CALL run_job(:job_id);
ERROR: insufficient permissions to run job 1000
\set ON_ERROR_STOP 1
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
-- This should succeed since the role belongs to the job owner group.
CALL run_job(:job_id);

View File

@ -4,6 +4,7 @@ include(GenerateTestSchedule)
# so unless you have a good reason, add new test files here.
set(TEST_FILES
bgw_custom.sql
bgw_security.sql
bgw_policy.sql
cagg_errors.sql
cagg_invalidation.sql

View File

@ -0,0 +1,38 @@
-- 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 ROLE_ADMIN :TEST_DBNAME _admin
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE ROLE :ROLE_ADMIN;
GRANT :ROLE_ADMIN TO :ROLE_DEFAULT_PERM_USER;
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE TABLE custom_log (ts integer, msg text);
GRANT ALL ON custom_log TO PUBLIC;
CREATE PROCEDURE custom_job(integer, jsonb) AS $$
INSERT INTO custom_log values($1, 'custom_job');
$$ LANGUAGE SQL;
SELECT add_job('custom_job', '1h') AS job_id \gset
-- Set the owner of the job to the admin role
UPDATE _timescaledb_config.bgw_job SET owner = :'ROLE_ADMIN' WHERE id = :job_id;
SELECT id, proc_name, owner FROM _timescaledb_config.bgw_job WHERE id = :job_id;
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER_2
-- We should fail to execute the job since we do not own it or belong
-- to the group that owns it.
\set ON_ERROR_STOP 0
CALL run_job(:job_id);
\set ON_ERROR_STOP 1
\c :TEST_DBNAME :ROLE_DEFAULT_PERM_USER
-- This should succeed since the role belongs to the job owner group.
CALL run_job(:job_id);