Noções básicas sobre o comportamento de mascaramento nas funções de gatilho
Quando políticas pg_columnmask são aplicadas às tabelas, é importante entender como o mascaramento interage com as funções de gatilho. Gatilhos são funções de banco de dados que são executadas automaticamente em resposta a determinados eventos em uma tabela, como as operações INSERT, UPDATE ou DELETE.
Por padrão, o DDM aplica regras de mascaramento diferentes, dependendo do tipo de gatilho:
- Gatilhos de tabela
Tabelas de transição não são mascaradas: as funções de gatilho nas tabelas têm acesso aos dados não mascarados em suas tabelas de transição para versões de linha antigas e novas.
Os proprietários de tabelas criam gatilhos e são proprietários dos dados, para que tenham acesso total para gerenciar suas tabelas de forma eficaz
- Visualizar gatilhos (gatilhos INSTEAD OF)
As tabelas de transição são mascaradas: as funções de gatilho nas visualizações veem os dados mascarados de acordo com as permissões do usuário atual.
Os proprietários das visualizações podem ser diferentes dos proprietários da tabela base e devem respeitar as políticas de mascaramento nas tabelas subjacentes.
Dois parâmetros de configuração em nível de servidor controlam o comportamento do gatilho com tabelas mascaradas. Eles só podem ser definidos por rds_superuser:
Restringir gatilhos em tabelas mascaradas: impede a execução de gatilhos quando um usuário mascarado executa operações do DML em tabelas com políticas de mascaramento aplicáveis.
Restringir gatilhos em visualizações com tabelas mascaradas: impede a execução de gatilhos em visualizações quando a definição da visualização inclui tabelas com políticas de mascaramento aplicáveis ao usuário atual.
exemplo das diferenças entre a aplicação da função à tabela e à visualização
O exemplo a seguir cria uma função de gatilho que imprime valores de linha antigos e novos e, depois, demonstra como a mesma função se comporta de forma diferente quando anexada a uma tabela em comparação a uma visualização.
-- 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;
Recomendamos revisar o comportamento dos gatilhos antes de implementá-los em tabelas mascaradas. Os gatilhos de tabela têm acesso a dados não mascarados em tabelas em transição, enquanto os gatilhos de visualização veem dados mascarados.
exemplo de renomear uma política de mascaramento
O exemplo a seguir demonstra como renomear políticas existentes usando o procedimento 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
exemplo de alterar o peso da política
O exemplo a seguir demonstra como alterar o peso da política para alterar seu 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
exemplo de como limpar
O exemplo a seguir demonstra como eliminar todas as políticas, tabelas e usuários.
-- 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;