Repository navigation
fix: add row level security over shared tables #303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
5ab51d8
95eb4d8
5d95f4a
c3d5ae3
a2d966e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,18 @@ create unlogged table net.http_request_queue( | |
| url text not null, | ||
| headers jsonb, | ||
| body bytea, | ||
| timeout_milliseconds int not null | ||
| timeout_milliseconds int not null, | ||
| rolename text not null default 'public' | ||
| ); | ||
| alter table net.http_request_queue enable row level security; | ||
|
|
||
| create policy net_request_policy | ||
| on net.http_request_queue | ||
| using ( | ||
| case | ||
| when rolename = 'public' then true | ||
| else pg_has_role(current_user, rolename, 'member') | ||
| end | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also correct me if I am wrong, dont we need any upgrade script ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those changes def need to be made and would not merge without that. |
||
| ); | ||
|
|
||
| create or replace function net.check_worker_is_up() returns void as $$ | ||
|
|
@@ -40,11 +51,22 @@ create unlogged table net._http_response( | |
| content text, | ||
| timed_out bool, | ||
| error_msg text, | ||
| created timestamptz not null default now() | ||
| created timestamptz not null default now(), | ||
| rolename text not null | ||
| ); | ||
| alter table net._http_response enable row level security; | ||
|
|
||
| create index on net._http_response (created); | ||
|
|
||
| create policy net_response_policy | ||
| on net._http_response | ||
| using ( | ||
| case | ||
| when rolename = 'public' then true | ||
| else pg_has_role(current_user, rolename, 'member') | ||
| end | ||
| ); | ||
|
|
||
| -- Blocks until an http_request is complete | ||
| -- API: Private | ||
| create or replace function net._await_response( | ||
|
|
@@ -116,7 +138,9 @@ create or replace function net.http_get( | |
| -- key/values to be included in request headers | ||
| headers jsonb default '{}'::jsonb, | ||
| -- the maximum number of milliseconds the request may take before being cancelled | ||
| timeout_milliseconds int default 5000 | ||
| timeout_milliseconds int default 5000, | ||
| -- the role that should have access to the request/response data | ||
| rolename text default 'public' | ||
| ) | ||
| -- request_id reference | ||
| returns bigint | ||
|
|
@@ -131,12 +155,13 @@ begin | |
| from jsonb_each_text(params); | ||
|
|
||
| -- Add to the request queue | ||
| insert into net.http_request_queue(method, url, headers, timeout_milliseconds) | ||
| insert into net.http_request_queue(method, url, headers, timeout_milliseconds, rolename) | ||
| values ( | ||
| 'GET', | ||
| net._encode_url_with_params_array(url, params_array), | ||
| headers, | ||
| timeout_milliseconds | ||
| timeout_milliseconds, | ||
| rolename | ||
| ) | ||
| returning id | ||
| into request_id; | ||
|
|
@@ -159,7 +184,9 @@ create or replace function net.http_post( | |
| -- key/values to be included in request headers | ||
| headers jsonb default '{"Content-Type": "application/json"}'::jsonb, | ||
| -- the maximum number of milliseconds the request may take before being cancelled | ||
| timeout_milliseconds int DEFAULT 5000 | ||
| timeout_milliseconds int DEFAULT 5000, | ||
| -- the role that should have access to the request/response data | ||
| rolename text default 'public' | ||
| ) | ||
| -- request_id reference | ||
| returns bigint | ||
|
|
@@ -200,13 +227,14 @@ begin | |
| jsonb_each_text(params); | ||
|
|
||
| -- Add to the request queue | ||
| insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds) | ||
| insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds, rolename) | ||
| values ( | ||
| 'POST', | ||
| net._encode_url_with_params_array(url, params_array), | ||
| headers, | ||
| convert_to(body::text, 'UTF8'), | ||
| timeout_milliseconds | ||
| timeout_milliseconds, | ||
| rolename | ||
| ) | ||
| returning id | ||
| into request_id; | ||
|
|
@@ -229,7 +257,9 @@ create or replace function net.http_delete( | |
| -- the maximum number of milliseconds the request may take before being cancelled | ||
| timeout_milliseconds int default 5000, | ||
| -- optional body of the request | ||
| body jsonb default NULL | ||
| body jsonb default NULL, | ||
| -- the role that should have access to the request/response data | ||
| rolename text default 'public' | ||
| ) | ||
| -- request_id reference | ||
| returns bigint | ||
|
|
@@ -244,13 +274,14 @@ begin | |
| from jsonb_each_text(params); | ||
|
|
||
| -- Add to the request queue | ||
| insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds) | ||
| insert into net.http_request_queue(method, url, headers, body, timeout_milliseconds, rolename) | ||
| values ( | ||
| 'DELETE', | ||
| net._encode_url_with_params_array(url, params_array), | ||
| headers, | ||
| convert_to(body::text, 'UTF8'), | ||
| timeout_milliseconds | ||
| timeout_milliseconds, | ||
| rolename | ||
| ) | ||
| returning id | ||
| into request_id; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,6 +31,7 @@ typedef struct { | |
| int32 timeout_milliseconds; | ||
| NullableDatum headersBin; | ||
| NullableDatum bodyBin; | ||
| Datum rolename; | ||
| } RequestQueueRow; | ||
|
|
||
| // The curl easy handle plus additional data, this acts for both the request and | ||
|
|
@@ -45,6 +46,7 @@ typedef struct { | |
| char *method; | ||
| CURL *ez_handle; | ||
| char *rejected_reason; // set when the request must not be sent | ||
| char *rolename; // set when the request must not be sent | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: remove the comment . I think it got pasted from above. |
||
| } CurlHandle; | ||
|
|
||
| uint64 delete_expired_responses(char *ttl, int batch_size); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| def test_queue_table_insert_default(conn) -> None: | ||
| sql = "CREATE USER test_user;" | ||
| conn.execute(sql) | ||
|
|
||
| sql = """ | ||
| insert into net.http_request_queue(method, url, headers, timeout_milliseconds) | ||
| values ( | ||
| 'GET', | ||
| 'http://localhost:8080', | ||
| '{}'::jsonb, | ||
| 5000 | ||
| ) | ||
| returning id; | ||
| """ | ||
| id = conn.execute(sql).fetchall()[0][0] | ||
| conn.commit() | ||
| sql = f"SELECT * FROM net.http_request_queue where id = {id};" | ||
| response = conn.execute(sql).fetchall() | ||
| assert len(response) == 1 | ||
| sql = "set role test_user;" | ||
| response = conn.execute(sql) | ||
| sql = f"SELECT * FROM net.http_request_queue where id = {id};" | ||
| response = conn.execute(sql).fetchall() | ||
| assert len(response) == 1 | ||
| sql = "RESET ROLE;" | ||
| response = conn.execute(sql) | ||
| sql = "DROP ROLE test_user;" | ||
| conn.execute(sql) | ||
| conn.commit() | ||
|
|
||
|
|
||
| def test_queue_table_insert_non_default(conn) -> None: | ||
| sql = "CREATE role test_role;" | ||
| conn.execute(sql) | ||
| sql = "CREATE USER test_user;" | ||
| conn.execute(sql) | ||
| sql = "GRANT test_role to test_user;" | ||
| conn.execute(sql) | ||
|
|
||
| sql = """ | ||
| insert into net.http_request_queue(method, url, headers, timeout_milliseconds, rolename) | ||
| values ( | ||
| 'GET', | ||
| 'http://localhost:8080', | ||
| '{}'::jsonb, | ||
| 5000, | ||
| 'test_role' | ||
| ) | ||
| returning id; | ||
| """ | ||
| id = conn.execute(sql).fetchall()[0][0] | ||
| conn.commit() | ||
| sql = f"SELECT * FROM net.http_request_queue where id = {id};" | ||
| response = conn.execute(sql).fetchall() | ||
| assert len(response) == 1 | ||
| sql = f"set role test_user;" | ||
| conn.execute(sql) | ||
| sql = f"SELECT * FROM net.http_request_queue where id = {id};" | ||
| response = conn.execute(sql).fetchall() | ||
| assert len(response) == 1 | ||
|
|
||
| sql = "RESET ROLE" | ||
| conn.execute(sql) | ||
|
|
||
| sql = "REVOKE test_role FROM test_user;" | ||
| conn.execute(sql) | ||
|
|
||
| sql = "set role test_user;" | ||
| conn.execute(sql) | ||
|
|
||
| sql = f"SELECT * FROM net.http_request_queue where id = {id};" | ||
| response = conn.execute(sql).fetchall() | ||
| assert len(response) == 0 | ||
| sql = "RESET ROLE;" | ||
| conn.execute(sql) | ||
| sql = "DROP ROLE test_user;" | ||
| conn.execute(sql) | ||
| sql = "DROP ROLE test_role;" | ||
| conn.execute(sql) | ||
| conn.commit() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndrewJackson2020 just adding here for completeness. We need to make required changes here too : https://github.com/supabase/postgres/blob/develop/ansible/files/admin_api_scripts/pg_upgrade_scripts/complete.sh#L157 to handle the migration scripts correctly