Skip to content

Commit

Permalink
Reduce privilege requirement in bgw_security
Browse files Browse the repository at this point in the history
The test `bgw_security` requires escalated privileges to modify
`_timescaledb_config.bgw_job` and set the owner, which does not work
when running tests on an instance without `SUPERUSER` privileges.

Reduce the requirements for the test by switching role before adding
the job, and hence avoid the need to update
`_timescaledb_config.bgw_job` to set the owner.

Commit also clarifies one error message generated on permission errors
for a job.
  • Loading branch information
mkindahl committed Feb 20, 2025
1 parent f7e626b commit afddde3
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 11 deletions.
2 changes: 1 addition & 1 deletion tsl/src/bgw_policy/job_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ job_delete(PG_FUNCTION_ARGS)
if (!has_privs_of_role(GetUserId(), job->fd.owner))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("insufficient permissions to delete job for user \"%s\"",
errmsg("insufficient permissions to delete job owned by \"%s\"",
GetUserNameFromId(job->fd.owner, false))));

ts_bgw_job_delete_by_id(job_id);
Expand Down
21 changes: 16 additions & 5 deletions tsl/test/expected/bgw_security.out
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,41 @@
-- LICENSE-TIMESCALE for a copy of the license.
\set ROLE_ADMIN :TEST_DBNAME _admin
\c :TEST_DBNAME :ROLE_SUPERUSER
CREATE ROLE :ROLE_ADMIN;
CREATE ROLE :ROLE_ADMIN LOGIN;
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;
SET ROLE :ROLE_ADMIN;
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;
RESET ROLE;
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.
-- We should fail to execute and delete 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
SELECT delete_job(:job_id);
ERROR: insufficient permissions to delete job owned by "db_bgw_security_admin"
\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);
-- This should succeed since we belong to the owners role.
SELECT delete_job(:job_id);
delete_job
------------

(1 row)

\c :TEST_DBNAME :ROLE_SUPERUSER
DROP ROLE :ROLE_ADMIN;
17 changes: 12 additions & 5 deletions tsl/test/sql/bgw_security.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
\set ROLE_ADMIN :TEST_DBNAME _admin
\c :TEST_DBNAME :ROLE_SUPERUSER

CREATE ROLE :ROLE_ADMIN;
CREATE ROLE :ROLE_ADMIN LOGIN;
GRANT :ROLE_ADMIN TO :ROLE_DEFAULT_PERM_USER;

\c :TEST_DBNAME :ROLE_SUPERUSER
Expand All @@ -17,22 +17,29 @@ CREATE PROCEDURE custom_job(integer, jsonb) AS $$
INSERT INTO custom_log values($1, 'custom_job');
$$ LANGUAGE SQL;

SET ROLE :ROLE_ADMIN;
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;
RESET ROLE;

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.
-- We should fail to execute and delete 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);
SELECT delete_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);

-- This should succeed since we belong to the owners role.
SELECT delete_job(:job_id);

\c :TEST_DBNAME :ROLE_SUPERUSER
DROP ROLE :ROLE_ADMIN;

0 comments on commit afddde3

Please sign in to comment.