diff --git a/docs/en_US/images/rls_policy_commands_tab.png b/docs/en_US/images/rls_policy_commands_tab.png index 1a1ef4d0f..f6dd0f551 100644 Binary files a/docs/en_US/images/rls_policy_commands_tab.png and b/docs/en_US/images/rls_policy_commands_tab.png differ diff --git a/docs/en_US/images/rls_policy_general_tab.png b/docs/en_US/images/rls_policy_general_tab.png index 17bd869db..1356cc569 100644 Binary files a/docs/en_US/images/rls_policy_general_tab.png and b/docs/en_US/images/rls_policy_general_tab.png differ diff --git a/docs/en_US/images/rls_policy_sql_tab.png b/docs/en_US/images/rls_policy_sql_tab.png index e9f6a47f0..c3e41b0ee 100644 Binary files a/docs/en_US/images/rls_policy_sql_tab.png and b/docs/en_US/images/rls_policy_sql_tab.png differ diff --git a/docs/en_US/rls_policy_dialog.rst b/docs/en_US/rls_policy_dialog.rst index 3796e0f20..53373975e 100644 --- a/docs/en_US/rls_policy_dialog.rst +++ b/docs/en_US/rls_policy_dialog.rst @@ -20,6 +20,7 @@ Use the fields in the *General* tab to define the RLS Policy: * Use the *Name* field to add a descriptive name for the RLS Policy. The name will be displayed in the *pgAdmin* tree control. * Use the drop-down listbox next to *Role* to select the Role to which the RLS Policy is to be applied. +* Use the drop-down listbox next to *Type* to select the type of the policy. Click the *Commands* tab to continue. @@ -31,7 +32,7 @@ Use the fields in the *Commands* tab to define the RLS Policy: * Use the drop-down listbox next to *Event* to select the command to which policy applies. Valid options are ALL, SELECT, INSERT, UPDATE, and DELETE. Default is ALL. * Use the *Using* field to add a SQL conditional expression returning boolean. This expression will be added to queries that refer to the table if row level security is enabled. -* Use the *With Check* field to add a SQL conditional expression returning boolean. This expression will be used in INSERT and UPDATE queries against the table if row level security is enabled. +* Use the *With check* field to add a SQL conditional expression returning boolean. This expression will be used in INSERT and UPDATE queries against the table if row level security is enabled. Click the *SQL* tab to continue. diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py index f7c9aa6b8..6a50937bc 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/__init__.py @@ -605,7 +605,7 @@ class RowSecurityView(PGChildNodeView): did = kwargs.get('did') scid = kwargs.get('scid') tid = kwargs.get('tid') - oid = kwargs.get('oid') + oid = kwargs.get('plid') data = kwargs.get('data', None) diff_schema = kwargs.get('diff_schema', None) drop_req = kwargs.get('drop_req', False) @@ -711,7 +711,7 @@ class RowSecurityView(PGChildNodeView): diff_dict = directory_diff( source, target, difference={} ) - if 'event' in diff_dict: + if 'event' in diff_dict or 'type' in diff_dict: delete_sql = self.get_sql_from_diff(gid=1, sid=tgt_params['sid'], did=tgt_params['did'], diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/static/js/row_security_policy.js b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/static/js/row_security_policy.js index fe96f81ab..ee866ecd7 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/static/js/row_security_policy.js +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/static/js/row_security_policy.js @@ -84,6 +84,7 @@ define('pgadmin.node.row_security_policy', [ using_orig: undefined, withcheck: undefined, withcheck_orig: undefined, + type:'PERMISSIVE', }, schema: [{ id: 'name', label: gettext('Name'), cell: 'string', @@ -148,7 +149,29 @@ define('pgadmin.node.row_security_policy', [ }); return res; }, - }], + }, + { + id: 'type', label: gettext('Type'), control: 'select2', deps:['type'], + type: 'text',readonly: function(m) { + return !m.isNew();}, + select2: { + width: '100%', + allowClear: false, + }, + options:[ + {label: 'PERMISSIVE', value: 'PERMISSIVE'}, + {label: 'RESTRICTIVE', value: 'RESTRICTIVE'}, + ], + visible: function(m) { + if(!_.isUndefined(m.node_info) && !_.isUndefined(m.node_info.server) + && !_.isUndefined(m.node_info.server.version) && + m.node_info.server.version >= 100000) + return true; + + return false; + }, + }, + ], validate: function(keys) { var msg; this.errorModel.clear(); diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy.sql new file mode 100644 index 000000000..9a3f6443d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy.sql @@ -0,0 +1,10 @@ +-- POLICY: policy_1 + +-- DROP POLICY policy_1 ON public.test_rls_policy; + +CREATE POLICY policy_1 + ON public.test_rls_policy + AS PERMISSIVE + FOR ALL + TO public; + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy_msql.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy_msql.sql new file mode 100644 index 000000000..ae36ebff1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/alter_policy_msql.sql @@ -0,0 +1,2 @@ +ALTER POLICY test ON public.test_rls_policy + RENAME TO policy_1; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_all_event_policy.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_all_event_policy.sql new file mode 100644 index 000000000..b7fafe327 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_all_event_policy.sql @@ -0,0 +1,12 @@ +-- POLICY: all_event_policy + +-- DROP POLICY all_event_policy ON public.test_rls_policy; + +CREATE POLICY all_event_policy + ON public.test_rls_policy + AS RESTRICTIVE + FOR ALL + TO public + USING (true) + WITH CHECK (true); + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_insert_policy.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_insert_policy.sql new file mode 100644 index 000000000..a404764f6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_insert_policy.sql @@ -0,0 +1,10 @@ +-- POLICY: insert_policy + +-- DROP POLICY insert_policy ON public.test_rls_policy; + +CREATE POLICY insert_policy + ON public.test_rls_policy + AS PERMISSIVE + FOR INSERT + TO public; + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_public_policy.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_public_policy.sql new file mode 100644 index 000000000..a693967f0 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_public_policy.sql @@ -0,0 +1,10 @@ +-- POLICY: test + +-- DROP POLICY test ON public.test_rls_policy; + +CREATE POLICY test + ON public.test_rls_policy + AS PERMISSIVE + FOR ALL + TO public; + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_select_policy.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_select_policy.sql new file mode 100644 index 000000000..64f449e1c --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/create_select_policy.sql @@ -0,0 +1,10 @@ +-- POLICY: select_policy + +-- DROP POLICY select_policy ON public.test_rls_policy; + +CREATE POLICY select_policy + ON public.test_rls_policy + AS PERMISSIVE + FOR SELECT + TO public; + diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/test.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/test.json new file mode 100644 index 000000000..02455a8a6 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/10_plus/test.json @@ -0,0 +1,105 @@ +{ + "scenarios": [ + { + "type": "create", + "name": "Create Table For RLS policy", + "endpoint": "NODE-table.obj", + "sql_endpoint": "NODE-table.sql_id", + "data": { + "name": "test_rls_policy", + "columns": [ + { + "name": "emp_id", + "cltype": "integer", + "is_primary_key": true + }, + { + "name": "name", + "cltype": "text" + }, + { + "name": "salary", + "cltype": "bigint" + } + ], + "is_partitioned": false, + "schema": "public", + "spcname": "pg_default" + }, + "store_object_id": true + }, + { + "type": "create", + "name": "Create select RLS policy", + "endpoint": "NODE-row_security_policy.obj", + "sql_endpoint": "NODE-row_security_policy.sql_id", + "data": { + "name": "select_policy", + "event": "SELECT", + "policyowner": "public", + "schema": "public" + }, + "expected_sql_file": "create_select_policy.sql" + }, + { + "type": "create", + "name": "Create INSERT RLS policy", + "endpoint": "NODE-row_security_policy.obj", + "sql_endpoint": "NODE-row_security_policy.sql_id", + "data": { + "name": "insert_policy", + "event": "INSERT", + "policyowner": "public", + "schema": "public" + }, + "expected_sql_file": "create_insert_policy.sql" + }, + { + "type": "create", + "name": "Create RLS policy", + "endpoint": "NODE-row_security_policy.obj", + "sql_endpoint": "NODE-row_security_policy.sql_id", + "data": { + "name": "test", + "schema": "public" + }, + "expected_sql_file": "create_public_policy.sql" + }, + { + "type": "alter", + "name": "Alter policy name", + "endpoint": "NODE-row_security_policy.obj_id", + "sql_endpoint": "NODE-row_security_policy.sql_id", + "msql_endpoint": "NODE-row_security_policy.msql_id", + "data": { + "name": "policy_1" + }, + "expected_sql_file": "alter_policy.sql", + "expected_msql_file": "alter_policy_msql.sql" + }, + { + "type": "create", + "name": "Create RLS policy for event 'ALL'", + "endpoint": "NODE-row_security_policy.obj", + "sql_endpoint": "NODE-row_security_policy.sql_id", + "data": { + "name": "all_event_policy", + "event": "ALL", + "policyowner": "public", + "schema": "public", + "using": "true", + "withcheck": "true", + "type":"RESTRICTIVE" + }, + "expected_sql_file": "create_all_event_policy.sql" + }, + { + "type": "delete", + "name": "Drop policy", + "endpoint": "NODE-row_security_policy.delete_id", + "data": { + "name": "test_delete_policy_$%{}[]()&*^!@\"'`\\/#" + } + } + ] +} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/default/test.json b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/default/test.json index 5acdde798..02455a8a6 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/default/test.json +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/row_security_policies/tests/default/test.json @@ -88,7 +88,8 @@ "policyowner": "public", "schema": "public", "using": "true", - "withcheck": "true" + "withcheck": "true", + "type":"RESTRICTIVE" }, "expected_sql_file": "create_all_event_policy.sql" }, diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/create.sql new file mode 100644 index 000000000..27c7c3741 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/create.sql @@ -0,0 +1,27 @@ +{% set add_semicolon_after = 'to' %} +{% if data.withcheck is defined and data.withcheck != None and data.withcheck != '' %} +{% set add_semicolon_after = 'with_check' %} +{% elif data.using is defined and data.using != None and data.using != '' %} +{% set add_semicolon_after = 'using' %} +{% endif %} +CREATE POLICY {{ conn|qtIdent(data.name) }} + ON {{conn|qtIdent(data.schema, data.table)}} +{%if data.type %} + AS {{ data.type|upper }} +{% endif %} +{% if data.event %} + FOR {{ data.event|upper }} +{% endif %} +{% if data.policyowner %} + TO {{ conn|qtTypeIdent(data.policyowner) }}{% if add_semicolon_after == 'to' %};{% endif %} +{% else %} + TO public{% if add_semicolon_after == 'to' %};{% endif %} +{% endif %} +{% if data.using %} + + USING ({{ data.using }}){% if add_semicolon_after == 'using' %};{% endif %} +{% endif %} +{% if data.withcheck %} + + WITH CHECK ({{ data.withcheck }}); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/properties.sql new file mode 100644 index 000000000..c3bc2b12d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/10_plus/properties.sql @@ -0,0 +1,22 @@ +SELECT + pl.oid AS oid, + pl.polname AS name, + rw.permissive as type, + rw.cmd AS event, + rw.qual AS using, + rw.qual AS using_orig, + rw.with_check AS withcheck, + rw.with_check AS withcheck_orig, + + array_to_string(rw.roles::name[], ', ') AS policyowner +FROM + pg_policy pl +JOIN pg_policies rw ON pl.polname=rw.policyname +JOIN pg_namespace n ON n.nspname=rw.schemaname +WHERE +{% if plid %} + pl.oid = {{ plid }} and n.oid = {{ scid }}; +{% endif %} +{% if tid %} + pl.polrelid = {{ tid }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/create.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/create.sql new file mode 100644 index 000000000..67c03794e --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/create.sql @@ -0,0 +1,24 @@ +{% set add_semicolon_after = 'to' %} +{% if data.withcheck is defined and data.withcheck != None and data.withcheck != '' %} +{% set add_semicolon_after = 'with_check' %} +{% elif data.using is defined and data.using != None and data.using != '' %} +{% set add_semicolon_after = 'using' %} +{% endif %} +CREATE POLICY {{ conn|qtIdent(data.name) }} + ON {{conn|qtIdent(data.schema, data.table)}} +{% if data.event %} + FOR {{ data.event|upper }} +{% endif %} +{% if data.policyowner %} + TO {{ conn|qtTypeIdent(data.policyowner) }}{% if add_semicolon_after == 'to' %};{% endif %} +{% else %} + TO public{% if add_semicolon_after == 'to' %};{% endif %} +{% endif %} +{% if data.using %} + + USING ({{ data.using }}){% if add_semicolon_after == 'using' %};{% endif %} +{% endif %} +{% if data.withcheck %} + + WITH CHECK ({{ data.withcheck }}); +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/delete.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/delete.sql new file mode 100644 index 000000000..586e114b1 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/delete.sql @@ -0,0 +1 @@ +DROP POLICY {{ conn|qtIdent(policy_name) }} ON {{conn|qtIdent(result.schema, result.table)}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_parent.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_parent.sql new file mode 100644 index 000000000..aad3fa523 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_parent.sql @@ -0,0 +1,5 @@ +SELECT nsp.nspname AS schema ,rel.relname AS table +FROM pg_class rel + JOIN pg_namespace nsp + ON rel.relnamespace = nsp.oid::oid + WHERE rel.oid = {{tid}}::oid diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_policy_name.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_policy_name.sql new file mode 100644 index 000000000..82bcb51ec --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_policy_name.sql @@ -0,0 +1,9 @@ +{% if plid %} +SELECT + pl.oid AS oid, + pl.polname AS name +FROM + pg_policy pl +WHERE + pl.oid = {{ plid }} +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_position.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_position.sql new file mode 100644 index 000000000..0d67b1107 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/get_position.sql @@ -0,0 +1,2 @@ +SELECT pl.oid FROM pg_policy pl +WHERE pl.polrelid = {{tid}}::oid AND pl.polname = {{data.name|qtLiteral}}; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/nodes.sql new file mode 100644 index 000000000..083c01605 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/nodes.sql @@ -0,0 +1,13 @@ +SELECT + pl.oid AS oid, + pl.polname AS name +FROM + pg_policy pl +WHERE +{% if tid %} + pl.polrelid = {{ tid }} +{% elif plid %} + pl.oid = {{ plid }} +{% endif %} +ORDER BY + pl.polname; diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/properties.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/properties.sql new file mode 100644 index 000000000..d69776194 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/properties.sql @@ -0,0 +1,21 @@ +SELECT + pl.oid AS oid, + pl.polname AS name, + rw.cmd AS event, + rw.qual AS using, + rw.qual AS using_orig, + rw.with_check AS withcheck, + rw.with_check AS withcheck_orig, + + array_to_string(rw.roles::name[], ', ') AS policyowner +FROM + pg_policy pl +JOIN pg_policies rw ON pl.polname=rw.policyname +JOIN pg_namespace n ON n.nspname=rw.schemaname +WHERE +{% if plid %} + pl.oid = {{ plid }} and n.oid = {{ scid }}; +{% endif %} +{% if tid %} + pl.polrelid = {{ tid }}; +{% endif %} diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/update.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/update.sql new file mode 100644 index 000000000..619f0038d --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/row_security_policies/sql/default/update.sql @@ -0,0 +1,33 @@ +{#####################################################} +{## Change policy owner ##} +{#####################################################} +{% if data.policyowner and o_data.policyowner != data.policyowner %} +ALTER POLICY {{ o_data.name }} ON {{conn|qtIdent(o_data.schema, o_data.table)}} + TO {{ conn|qtTypeIdent(data.policyowner) }}; +{% endif %} + +{#####################################################} +{## Change policy using condition ##} +{#####################################################} +{% if data.using and o_data.using != data.using %} +ALTER POLICY {{ o_data.name }} ON {{conn|qtIdent(o_data.schema, o_data.table)}} + USING ({{ data.using }}); +{% endif %} + +{#####################################################} +{## Change policy with check condition ##} +{#####################################################} +{% if data.withcheck and o_data.withcheck != data.withcheck %} +ALTER POLICY {{ o_data.name }} ON {{conn|qtIdent(o_data.schema, o_data.table)}} + WITH CHECK ({{ data.withcheck }}); +{% endif %} + +{#####################################################} +{## Change policy name ##} +{#####################################################} +{% if data.name and o_data.name != data.name %} +ALTER POLICY {{ o_data.name }} ON {{conn|qtIdent(o_data.schema, o_data.table)}} + RENAME TO {{ conn|qtIdent(data.name) }}; +{% endif %} + +