From 1804be5a5f421eb878ee75cc973f142c0d388726 Mon Sep 17 00:00:00 2001 From: Eric Davies Date: Thu, 28 Nov 2024 22:00:03 -0600 Subject: [PATCH] WIP policy_is_permissive, policy_is_restrictive --- sql/pgtap.sql.in | 101 ++++++++++++++++++++++++++++++++++++++++++ test/sql/policy.sql | 104 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 1 deletion(-) diff --git a/sql/pgtap.sql.in b/sql/pgtap.sql.in index 5f4cd1592..156014a82 100644 --- a/sql/pgtap.sql.in +++ b/sql/pgtap.sql.in @@ -10624,6 +10624,107 @@ RETURNS TEXT AS $$ ); $$ LANGUAGE sql; +/* +* Internal function to test whether the specified policy on the specified +* table in the specified schema is permissive (true) or restrictive (false). +*/ +CREATE OR REPLACE FUNCTION _permissive( NAME, NAME, NAME ) +RETURNS BOOLEAN AS $$ + -- restrictive policies did not exist before postgresql 10 + SELECT CASE WHEN pg_version_num() >= 100000 THEN pp.polpermissive ELSE TRUE END AS permissive + FROM pg_catalog.pg_policy AS pp + JOIN pg_catalog.pg_class AS pc ON pc.oid = pp.polrelid + JOIN pg_catalog.pg_namespace AS pn ON pn.oid = pc.relnamespace + WHERE pn.nspname = $1 + AND pc.relname = $2 + AND pp.polname = $3; +$$ LANGUAGE SQL; + +/* +* Internal function to test whether the specified policy on the specified +* table is permissive (true) or restrictive (false). +*/ +CREATE OR REPLACE FUNCTION _permissive( NAME, NAME ) +RETURNS BOOLEAN AS $$ + -- restrictive policies did not exist before postgresql 10 + SELECT CASE WHEN pg_version_num() >= 100000 THEN pp.polpermissive ELSE TRUE END AS permissive + FROM pg_catalog.pg_policy AS pp + JOIN pg_catalog.pg_class AS pc ON pc.oid = pp.polrelid + JOIN pg_catalog.pg_namespace AS pn ON pn.oid = pc.relnamespace + WHERE pc.relname = $1 + AND pp.polname = $2 + AND pn.nspname NOT IN ('pg_catalog', 'information_schema'); +$$ LANGUAGE SQL; + +-- policy_is_permissive( schema, table, policy, description ) +CREATE OR REPLACE FUNCTION policy_is_permissive( NAME, NAME, NAME, text ) +RETURNS TEXT AS $$ + SELECT ok( _permissive( $1, $2, $3 ), $4 ); +$$ LANGUAGE SQL; + +-- policy_is_permissive( schema, table, policy ) +CREATE OR REPLACE FUNCTION policy_is_permissive( NAME, NAME, NAME ) +RETURNS TEXT AS $$ + SELECT policy_is_permissive( + $1, $2, $3, + 'Policy ' || quote_ident($3) + || ' for table ' || quote_ident($1) || '.' || quote_ident($2) + || ' should be permissive, not restrictive' + ); +$$ LANGUAGE SQL; + +-- policy_is_permissive( table, policy, description ) +CREATE OR REPLACE FUNCTION policy_is_permissive( NAME, NAME, text ) +RETURNS TEXT AS $$ + SELECT ok( _permissive( $1, $2 ), $3 ); +$$ LANGUAGE SQL; + +-- policy_is_permissive( table, policy ) +CREATE OR REPLACE FUNCTION policy_is_permissive( NAME, NAME ) +RETURNS TEXT AS $$ + SELECT policy_is_permissive( + $1, $2, + 'Policy ' || quote_ident($2) + || ' for table ' || quote_ident($1) + || ' should be permissive, not restrictive' + ); +$$ LANGUAGE SQL; + +-- policy_is_restrictive( schema, table, policy, description ) +CREATE OR REPLACE FUNCTION policy_is_restrictive( NAME, NAME, NAME, text ) +RETURNS TEXT AS $$ + SELECT ok( NOT _permissive( $1, $2, $3 ), $4 ); +END; +$$ LANGUAGE SQL; + +-- policy_is_restrictive( schema, table, policy ) +CREATE OR REPLACE FUNCTION policy_is_restrictive( NAME, NAME, NAME ) +RETURNS TEXT AS $$ + SELECT policy_is_restrictive( + $1, $2, $3, + 'Policy ' || quote_ident($3) + || ' for table ' || quote_ident($1) || '.' || quote_ident($2) + || ' should be restrictive, not permissive' + ); +$$ LANGUAGE SQL; + +-- policy_is_restrictive( table, policy, description ) +CREATE OR REPLACE FUNCTION policy_is_restrictive( NAME, NAME, text ) +RETURNS TEXT AS $$ + SELECT ok( NOT _permissive( $1, $2 ), $3 ); +$$ LANGUAGE SQL; + +-- policy_is_restrictive( table, policy ) +CREATE OR REPLACE FUNCTION policy_is_restrictive( NAME, NAME ) +RETURNS TEXT AS $$ + SELECT policy_is_restrictive( + $1, $2, + 'Policy ' || quote_ident($2) + || ' for table ' || quote_ident($1) + || ' should be restrictive, not permissive' + ); +$$ LANGUAGE SQL; + /******************** INHERITANCE ***********************************************/ /* * Internal function to test whether the specified table in the specified schema diff --git a/test/sql/policy.sql b/test/sql/policy.sql index 59f34c318..9a116408d 100644 --- a/test/sql/policy.sql +++ b/test/sql/policy.sql @@ -1,7 +1,7 @@ \unset ECHO \i test/setup.sql -SELECT plan(180); +SELECT plan(192); --SELECT * FROM no_plan(); -- This will be rolled back. :-) @@ -579,6 +579,108 @@ SELECT * FROM check_test( want: DELETE' ); +/****************************************************************************/ +-- Test policy_is_permissive(). + +SELECT * FROM check_test( + policy_is_permissive( 'public', 'passwd', 'root_all'::NAME ), + true, + 'policy_is_permissive(schema, table, policy)', + 'Policy root_all for table public.passwd should be permissive, not restrictive', + '' +); + +SELECT * FROM check_test( + policy_is_permissive( 'public', 'passwd', 'doesnt_exist'::NAME ), + true, + 'policy_is_permissive(schema, table, policy) for nonexistent policy', + 'Policy root_all for table public.passwd should be permissive, not restrictive', + '(test result was NULL)' +); + +SELECT * FROM check_test( + policy_is_permissive( 'public', 'passwd', 'root_all', 'whatever' ), + true, + 'policy_is_permissive(schema, table, policy, desc)', + 'whatever', + '' +); + +SELECT * FROM check_test( + policy_is_permissive( 'passwd', 'root_all' ), + true, + 'policy_is_permissive(table, policy)', + 'Policy root_all for table passwd should be permissive, not restrictive', + '' +); + +SELECT * FROM check_test( + policy_is_permissive( 'passwd', 'doesnt_exist' ), + true, + 'policy_is_permissive(table, policy) for nonexistent policy', + 'Policy root_all for table passwd should be permissive, not restrictive', + '(test result was NULL)' +); + +SELECT * FROM check_test( + policy_is_permissive( 'passwd', 'root_all', 'whatever' ), + true, + 'policy_is_permissive(table, policy, desc)', + 'whatever', + '' +); + +/****************************************************************************/ +-- Test policy_is_restrictive(). + +SELECT * FROM check_test( + policy_is_restrictive( 'public', 'passwd', 'root_all'::NAME ), + false, + 'policy_is_permissive(schema, table, policy)', + 'Policy root_all for table public.passwd should be permissive, not restrictive', + '' +); + +SELECT * FROM check_test( + policy_is_restrictive( 'public', 'passwd', 'doesnt_exist'::NAME ), + false, + 'policy_is_permissive(schema, table, policy) for nonexistent policy', + 'Policy doesnt_exist for table public.passwd should be permissive, not restrictive', + '(test result was NULL)' +); + +SELECT * FROM check_test( + policy_is_restrictive( 'public', 'passwd', 'root_all', 'whatever' ), + false, + 'policy_is_permissive(schema, table, policy, desc)', + 'whatever', + '' +); + +SELECT * FROM check_test( + policy_is_restrictive( 'passwd', 'root_all' ), + false, + 'policy_is_permissive(table, policy)', + 'Policy root_all for table passwd should be permissive, not restrictive', + '' +); + +SELECT * FROM check_test( + policy_is_restrictive( 'passwd', 'doesnt_exist' ), + false, + 'policy_is_permissive(table, policy) for nonexistent policy', + 'Policy doesnt_exist for table passwd should be permissive, not restrictive', + '(test result was NULL)' +); + +SELECT * FROM check_test( + policy_is_restrictive( 'passwd', 'root_all', 'whatever' ), + false, + 'policy_is_permissive(table, policy, desc)', + 'whatever', + '' +); + /****************************************************************************/ -- Finish the tests and clean up. SELECT * FROM finish();