Comprensión del comportamiento de enmascaramiento en las funciones de activación
Cuando se aplican políticas de pg_columnmask a las tablas, es importante entender cómo interactúa el enmascaramiento con las funciones de activación. Los activadores son funciones de la base de datos que se ejecutan automáticamente en respuesta a determinados eventos de una tabla, como las operaciones INSERT, UPDATE o DELETE.
De forma predeterminada, DDM aplica diferentes reglas de enmascaramiento según el tipo de activador:
- Activadores de tablas
Las tablas de transición están desenmascaradas: las funciones activadoras de las tablas tienen acceso a los datos desenmascarados de sus tablas de transición en las versiones de filas antiguas y en las nuevas
Los propietarios de las tablas crean los activadores y son dueños de los datos, por lo que tienen acceso total para administrar sus tablas de manera eficaz
- Consulta de activadores (EN LUGAR DE los activadores)
Las tablas de transición están enmascaradas: las funciones de activación de las vistas ven los datos enmascarados según los permisos del usuario actual
Los propietarios de las vistas pueden diferir de los propietarios de la tabla base y deben respetar las políticas de enmascaramiento de las tablas subyacentes
Dos parámetros de configuración por servidor controlan el comportamiento de los activadores con tablas enmascaradas. Estos solo se pueden configurar mediante rds_superuser:
Restringir los activadores en las tablas enmascaradas: impide la ejecución de los activadores cuando un usuario enmascarado realiza operaciones de DML en tablas con las políticas de enmascaramiento aplicables.
Restringir los activadores en las vistas con tablas enmascaradas: impide la ejecución de los activadores en las vistas cuando la definición de la vista incluye tablas con políticas de enmascaramiento aplicables al usuario actual.
ejemplo de las diferencias entre la aplicación de la función a la tabla y la vista
El siguiente ejemplo crea una función de activación que imprime los valores de fila antiguos y nuevos y, a continuación, demuestra cómo la misma función se comporta de forma diferente cuando se adjunta a una tabla y no a una vista.
-- Create trigger function CREATE OR REPLACE FUNCTION print_changes() RETURNS TRIGGER AS $$ BEGIN RAISE NOTICE 'Old row: name=%, email=%, ssn=%, salary=%', OLD.name, OLD.email, OLD.ssn, OLD.salary; RAISE NOTICE 'New row: name=%, email=%, ssn=%, salary=%', NEW.name, NEW.email, NEW.ssn, NEW.salary; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Create trigger CREATE TRIGGER print_changes_trigger BEFORE UPDATE ON hr.employees FOR EACH ROW EXECUTE FUNCTION print_changes(); -- Grant update to analyst role GRANT UPDATE ON hr.employees TO analyst_role; -- Unmasked data must be seen inside trigger even for masked user for the OLD and NEW -- row passed to trigger function BEGIN; SET ROLE mike_analyst; UPDATE hr.employees SET id = id + 10 RETURNING *; NOTICE: Old row: name=John Doe, email=john.doe@example.com, ssn=123-45-6789, salary=50000.00 NOTICE: New row: name=John Doe, email=john.doe@example.com, ssn=123-45-6789, salary=50000.00 NOTICE: Old row: name=Jane Smith, email=jane.smith@example.com, ssn=987-65-4321, salary=60000.00 NOTICE: New row: name=Jane Smith, email=jane.smith@example.com, ssn=987-65-4321, salary=60000.00 id | name | email | ssn | salary ----+------------+------------------------+-------------+---------- 11 | John Doe | john.doe@example.com | XXX-XX-6789 | 45000.00 12 | Jane Smith | jane.smith@example.com | XXX-XX-4321 | 54000.00 (2 rows) ROLLBACK; -- Triggers on views (which are supposed to see masked data for new/old row) CREATE VIEW hr.view_over_employees AS SELECT * FROM hr.employees; GRANT UPDATE, SELECT ON hr.view_over_employees TO analyst_role; -- Create trigger for this view CREATE TRIGGER print_changes_trigger INSTEAD OF UPDATE ON hr.view_over_employees FOR EACH ROW EXECUTE FUNCTION print_changes(); -- Masked new and old rows should be passed to trigger if trigger is on view BEGIN; SET ROLE mike_analyst; UPDATE hr.view_over_employees SET id = id + 10 RETURNING *; NOTICE: Old row: name=John Doe, email=john.doe@example.com, ssn=XXX-XX-6789, salary=45000.00 NOTICE: New row: name=John Doe, email=john.doe@example.com, ssn=XXX-XX-6789, salary=45000.00 NOTICE: Old row: name=Jane Smith, email=jane.smith@example.com, ssn=XXX-XX-4321, salary=54000.00 NOTICE: New row: name=Jane Smith, email=jane.smith@example.com, ssn=XXX-XX-4321, salary=54000.00 id | name | email | ssn | salary ----+------------+------------------------+-------------+---------- 11 | John Doe | john.doe@example.com | XXX-XX-6789 | 45000.00 12 | Jane Smith | jane.smith@example.com | XXX-XX-4321 | 54000.00 (2 rows) ROLLBACK;
Se recomienda revisar el comportamiento de los activadores antes de implementarlos en las tablas enmascaradas. Los activadores de tablas tienen acceso a los datos desenmascarados de las tablas de transición, mientras que los activadores de visualización ven los datos enmascarados.
ejemplo de cambio de nombre de la política de enmascaramiento
En el siguiente ejemplo se muestra cómo cambiar el nombre de las políticas existentes mediante el procedimiento rename_masking_policy.
-- Rename the strict policy CALL pgcolumnmask.rename_masking_policy( 'employee_mask_strict', 'hr.employees', 'intern_protection_policy' ); -- Verify the rename SELECT policyname, roles, weight FROM pgcolumnmask.pg_columnmask_policies WHERE tablename = 'employees' ORDER BY weight DESC; policyname | roles | weight --------------------------+----------------+-------- employee_mask_light | {analyst_role} | 100 employee_mask_moderate | {support_role} | 50 intern_protection_policy | {intern_role} | 10
ejemplo de modificar el peso de las políticas
En el siguiente ejemplo se muestra cómo modificar las ponderaciones de las políticas para cambiar su peso.
-- Change weight of moderate policy CALL pgcolumnmask.alter_masking_policy( 'employee_mask_moderate'::NAME, 'hr.employees'::REGCLASS, NULL, -- Keep existing masking expressions NULL, -- Keep existing roles 75 -- New weight ); -- Verify the changes SELECT policyname, roles, weight FROM pgcolumnmask.pg_columnmask_policies WHERE tablename = 'employees' ORDER BY weight DESC; policyname | roles | weight --------------------------+----------------+-------- employee_mask_light | {analyst_role} | 100 employee_mask_moderate | {support_role} | 75 intern_protection_policy | {intern_role} | 10
ejemplo de limpieza
En el siguiente ejemplo se muestra cómo eliminar todas las políticas, tablas y usuarios.
-- Drop policies CALL pgcolumnmask.drop_masking_policy( 'intern_protection_policy', 'hr.employees' ); CALL pgcolumnmask.drop_masking_policy( 'employee_mask_moderate', 'hr.employees' ); CALL pgcolumnmask.drop_masking_policy( 'employee_mask_light', 'hr.employees' ); -- Drop table and functions DROP VIEW IF EXISTS hr.view_over_employees; DROP TABLE IF EXISTS hr.employees; DROP SCHEMA IF EXISTS hr; DROP FUNCTION IF EXISTS public.mask_ssn(text); DROP FUNCTION IF EXISTS public.mask_salary(numeric, numeric); -- Drop users DROP USER sarah_intern, lisa_support, mike_analyst, ethan_support_intern, john_analyst_intern; DROP ROLE intern_role, support_role, analyst_role;