diff --git a/proxy/dev/docker-compose.yml b/proxy/dev/docker-compose.yml new file mode 100644 index 0000000000..f1ec3eaef9 --- /dev/null +++ b/proxy/dev/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' + +services: + proxy-postgres: + image: postgres:17-bookworm + container_name: proxy-postgres + environment: + POSTGRES_PASSWORD: proxy-postgres + ports: + - "5432:5432" + volumes: + - ./sql:/docker-entrypoint-initdb.d diff --git a/proxy/dev/sql/include/neon_control_plane.sql b/proxy/dev/sql/include/neon_control_plane.sql new file mode 100644 index 0000000000..8b58b81b2b --- /dev/null +++ b/proxy/dev/sql/include/neon_control_plane.sql @@ -0,0 +1,12 @@ +-- docker exec -it proxy-postgres psql -U postgres -c "CREATE SCHEMA IF NOT EXISTS neon_control_plane" +-- docker exec -it proxy-postgres psql -U postgres -c "CREATE TABLE neon_control_plane.endpoints (endpoint_id VARCHAR(255) PRIMARY KEY, allowed_ips VARCHAR(255))" +-- docker exec -it proxy-postgres psql -U postgres -c "CREATE ROLE proxy WITH SUPERUSER LOGIN PASSWORD 'password';" + +CREATE SCHEMA IF NOT EXISTS neon_control_plane; + +CREATE TABLE IF NOT EXISTS neon_control_plane.endpoints ( + endpoint_id VARCHAR(255) PRIMARY KEY, + allowed_ips VARCHAR(255) +); + +-- CREATE ROLE proxy WITH SUPERUSER LOGIN PASSWORD 'password'; \ No newline at end of file diff --git a/proxy/dev/sql/include/pgrst.sql b/proxy/dev/sql/include/pgrst.sql new file mode 100644 index 0000000000..e7f51a9fe4 --- /dev/null +++ b/proxy/dev/sql/include/pgrst.sql @@ -0,0 +1,22 @@ + +-- code to monitor the last schema update +CREATE SCHEMA IF NOT EXISTS pgrst; + +ALTER ROLE authenticator SET pgrst.last_schema_updated = now()::text; +-- Create an event trigger function +CREATE OR REPLACE FUNCTION pgrst.pgrst_watch() RETURNS event_trigger + LANGUAGE sql + AS $$ + ALTER ROLE authenticator SET pgrst.last_schema_updated = now()::text; +$$; + +CREATE OR REPLACE FUNCTION pgrst.last_schema_updated() RETURNS text + LANGUAGE sql + AS $$ + SELECT current_setting('pgrst.last_schema_updated', true); +$$; + +-- This event trigger will fire after every ddl_command_end event +CREATE EVENT TRIGGER pgrst.pgrst_watch + ON ddl_command_end + EXECUTE PROCEDURE pgrst.pgrst_watch(); \ No newline at end of file diff --git a/proxy/dev/sql/include/roles.sql b/proxy/dev/sql/include/roles.sql new file mode 100644 index 0000000000..53f4cf402a --- /dev/null +++ b/proxy/dev/sql/include/roles.sql @@ -0,0 +1,124 @@ +CREATE ROLE authenticator LOGIN NOINHERIT; +CREATE ROLE anonymous noinherit; +GRANT ROLE anonymous TO authenticator; + +-- reloadable config options +-- these settings will override the values in configs/no-defaults.config, so they must be different +-- ALTER ROLE authenticator SET pgrst.db_aggregates_enabled = 'false'; +ALTER ROLE authenticator SET pgrst.db_anon_role = 'anonymous'; +ALTER ROLE authenticator SET pgrst.db_extra_search_path = 'public, extensions'; +ALTER ROLE authenticator SET pgrst.db_max_rows = '500'; +-- ALTER ROLE authenticator SET pgrst.db_plan_enabled = 'false'; +-- ALTER ROLE authenticator SET pgrst.db_pre_config = 'postgrest.preconf'; +-- ALTER ROLE authenticator SET pgrst.db_pre_request = 'test.custom_headers'; +-- ALTER ROLE authenticator SET pgrst.db_prepared_statements = 'false'; +-- ALTER ROLE authenticator SET pgrst.db_root_spec = 'root'; +ALTER ROLE authenticator SET pgrst.db_schemas = 'test, tenant1, tenant2'; +-- ALTER ROLE authenticator SET pgrst.db_tx_end = 'commit-allow-override'; +-- ALTER ROLE authenticator SET pgrst.jwt_aud = 'https://example.org'; +-- ALTER ROLE authenticator SET pgrst.jwt_cache_max_lifetime = '3600'; +ALTER ROLE authenticator SET pgrst.jwt_role_claim_key = '."role"'; +-- ALTER ROLE authenticator SET pgrst.jwt_secret = 'REALLY=REALLY=REALLY=REALLY=VERY=SAFE'; +-- ALTER ROLE authenticator SET pgrst.jwt_secret_is_base64 = 'false'; +ALTER ROLE authenticator SET pgrst.not_existing = 'should be ignored'; +-- ALTER ROLE authenticator SET pgrst.openapi_server_proxy_uri = 'https://example.org/api'; +-- ALTER ROLE authenticator SET pgrst.server_cors_allowed_origins = 'http://origin.com'; +-- ALTER ROLE authenticator SET pgrst.server_timing_enabled = 'false'; +-- ALTER ROLE authenticator SET pgrst.server_trace_header = 'CF-Ray'; +-- ALTER ROLE authenticator SET pgrst.db_hoisted_tx_settings = 'autovacuum_work_mem'; + +-- override with database specific setting +-- ALTER ROLE authenticator IN DATABASE :DBNAME SET pgrst.db_extra_search_path = 'public, extensions, private'; +-- ALTER ROLE authenticator IN DATABASE :DBNAME SET pgrst.jwt_secret = 'OVERRIDE=REALLY=REALLY=REALLY=REALLY=VERY=SAFE'; +-- ALTER ROLE authenticator IN DATABASE :DBNAME SET pgrst.not_existing = 'should be ignored'; + +-- other database settings that should be ignored +-- CREATE DATABASE other; +-- ALTER ROLE authenticator IN DATABASE other SET pgrst.db_max_rows = '1111'; + +-- non-reloadable configs +-- ALTER ROLE authenticator SET pgrst.admin_server_host = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.admin_server_port = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_channel = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_channel_enabled = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_config = 'true'; +-- ALTER ROLE authenticator SET pgrst.db_pool = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_pool_acquisition_timeout = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_pool_timeout = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_pool_max_idletime = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_pool_max_lifetime = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.db_uri = 'postgresql://ignored'; +-- ALTER ROLE authenticator SET pgrst.log_level = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.log_query = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.server_host = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.server_port = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.server_unix_socket = 'ignored'; +-- ALTER ROLE authenticator SET pgrst.server_unix_socket_mode = 'ignored'; + +-- other authenticator reloadable config options +-- these settings will override the values in configs/no-defaults.config, so they must be different +-- CREATE ROLE other_authenticator LOGIN NOINHERIT; +-- ALTER ROLE other_authenticator SET pgrst.db_aggregates_enabled = 'false'; +-- ALTER ROLE other_authenticator SET pgrst.db_extra_search_path = 'public, extensions, other'; +-- ALTER ROLE other_authenticator SET pgrst.db_max_rows = '100'; +-- ALTER ROLE other_authenticator SET pgrst.db_plan_enabled = 'true'; +-- ALTER ROLE other_authenticator SET pgrst.db_pre_config = 'postgrest.other_preconf'; +-- ALTER ROLE other_authenticator SET pgrst.db_pre_request = 'test.other_custom_headers'; +-- ALTER ROLE other_authenticator SET pgrst.db_prepared_statements = 'false'; +-- ALTER ROLE other_authenticator SET pgrst.db_root_spec = 'other_root'; +-- ALTER ROLE other_authenticator SET pgrst.db_schemas = 'test, other_tenant1, other_tenant2'; +-- ALTER ROLE other_authenticator SET pgrst.jwt_aud = 'https://otherexample.org'; +-- ALTER ROLE other_authenticator SET pgrst.jwt_secret = 'ODERREALLYREALLYREALLYREALLYVERYSAFE'; +-- ALTER ROLE other_authenticator SET pgrst.jwt_secret_is_base64 = 'false'; +-- ALTER ROLE other_authenticator SET pgrst.jwt_cache_max_lifetime = '7200'; +-- ALTER ROLE other_authenticator SET pgrst.openapi_mode = 'disabled'; +-- ALTER ROLE other_authenticator SET pgrst.openapi_security_active = 'false'; +-- ALTER ROLE other_authenticator SET pgrst.openapi_server_proxy_uri = 'https://otherexample.org/api'; +-- ALTER ROLE other_authenticator SET pgrst.server_cors_allowed_origins = 'http://otherorigin.com'; +-- ALTER ROLE other_authenticator SET pgrst.server_timing_enabled = 'true'; +-- ALTER ROLE other_authenticator SET pgrst.server_trace_header = 'traceparent'; +-- ALTER ROLE other_authenticator SET pgrst.db_hoisted_tx_settings = 'maintenance_work_mem'; + +-- create schema postgrest; +-- grant usage on schema postgrest to authenticator; +-- grant usage on schema postgrest to other_authenticator; + +-- -- pre-config hook +-- create or replace function postgrest.pre_config() +-- returns void as $$ +-- begin +-- if current_user = 'other_authenticator' then +-- perform +-- set_config('pgrst.jwt_role_claim_key', '."other"."pre_config_role"', true) +-- , set_config('pgrst.db_anon_role', 'pre_config_role', true) +-- , set_config('pgrst.db_schemas', 'will be overriden with the above ALTER ROLE.. db_schemas', true) +-- , set_config('pgrst.db_tx_end', 'rollback-allow-override', true); +-- else +-- null; +-- end if; +-- end $$ language plpgsql; + +-- create or replace function postgrest.preconf() +-- returns void as $$ +-- begin +-- null; +-- end $$ language plpgsql; + +-- create or replace function postgrest.other_preconf() +-- returns void as $$ +-- begin +-- perform postgrest.pre_config(); +-- end $$ language plpgsql; + +-- -- authenticator used for tests that manipulate statement timeout +-- CREATE ROLE timeout_authenticator LOGIN NOINHERIT; + +-- create function set_statement_timeout(role text, milliseconds int) returns void as $_$ +-- begin +-- execute format($$ +-- alter role %I set statement_timeout to %L; +-- $$, role, milliseconds); +-- end $_$ volatile security definer language plpgsql; + +-- -- authenticator used for test-independent database manipulation +-- CREATE ROLE meta_authenticator LOGIN NOINHERIT; diff --git a/proxy/dev/sql/include/tenant1.sql b/proxy/dev/sql/include/tenant1.sql new file mode 100644 index 0000000000..eaf0f48e1c --- /dev/null +++ b/proxy/dev/sql/include/tenant1.sql @@ -0,0 +1,18 @@ +CREATE SCHEMA IF NOT EXISTS tenant1; + +CREATE TABLE IF NOT EXISTS tenant1.items ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +INSERT INTO tenant1.items (name) VALUES + ('tenant1 item 1'), + ('tenant1 item 2'), + ('tenant1 item 3'); + + +CREATE ROLE tenant1_role NOINHERIT; +GRANT ROLE tenant1_role TO authenticator; + +GRANT USAGE ON SCHEMA tenant1 TO tenant1_role; +GRANT ALL ON ALL TABLES IN SCHEMA tenant1 TO tenant1_role; \ No newline at end of file diff --git a/proxy/dev/sql/include/tenant2.sql b/proxy/dev/sql/include/tenant2.sql new file mode 100644 index 0000000000..a566642677 --- /dev/null +++ b/proxy/dev/sql/include/tenant2.sql @@ -0,0 +1,18 @@ +CREATE SCHEMA IF NOT EXISTS tenant2; + +CREATE TABLE IF NOT EXISTS tenant2.items ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +INSERT INTO tenant2.items (name) VALUES + ('tenant2 item 1'), + ('tenant2 item 2'), + ('tenant2 item 3'); + + +CREATE ROLE tenant2_role NOINHERIT; +GRANT ROLE tenant2_role TO authenticator; + +GRANT USAGE ON SCHEMA tenant2 TO tenant2_role; +GRANT ALL ON ALL TABLES IN SCHEMA tenant2 TO tenant2_role; \ No newline at end of file diff --git a/proxy/dev/sql/include/test.sql b/proxy/dev/sql/include/test.sql new file mode 100644 index 0000000000..0184bc280a --- /dev/null +++ b/proxy/dev/sql/include/test.sql @@ -0,0 +1,17 @@ +CREATE SCHEMA IF NOT EXISTS test; + +CREATE TABLE IF NOT EXISTS test.items ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL +); + +INSERT INTO test.items (name) VALUES + ('test item 1'), + ('test item 2'), + ('test item 3'); + +CREATE ROLE test_role NOINHERIT; +GRANT ROLE test_role TO authenticator; + +GRANT USAGE ON SCHEMA test TO test_role; +GRANT ALL ON ALL TABLES IN SCHEMA test TO test_role; \ No newline at end of file diff --git a/proxy/dev/sql/init.sql b/proxy/dev/sql/init.sql new file mode 100644 index 0000000000..3b9c585d9c --- /dev/null +++ b/proxy/dev/sql/init.sql @@ -0,0 +1,6 @@ +\ir include/neon_control_plane.sql +\ir include/roles.sql +\ir include/test.sql +\ir include/tenant1.sql +\ir include/tenant2.sql +\ir include/pgrst.sql \ No newline at end of file