diff options
author | Wolfgang Walther | 2024-11-02 19:49:16 +0100 |
---|---|---|
committer | Wolfgang Walther | 2024-11-09 18:24:52 +0100 |
commit | db2d6a00abe5ec01c9e016d1ac0bc449d06e51ad (patch) | |
tree | 9d108dd2064916832360734ad37b67855c043562 | |
parent | 23c19a255fab8dc09105851748583272755c5db7 (diff) |
postgresqlPackages.anonymizer: make passthru.tests work with correct package
Same reasoning as commit before.
-rw-r--r-- | nixos/tests/pg_anonymizer.nix | 203 | ||||
-rw-r--r-- | pkgs/servers/sql/postgresql/ext/anonymizer.nix | 6 |
2 files changed, 114 insertions, 95 deletions
diff --git a/nixos/tests/pg_anonymizer.nix b/nixos/tests/pg_anonymizer.nix index b26e4dca0580..080b8deabbe8 100644 --- a/nixos/tests/pg_anonymizer.nix +++ b/nixos/tests/pg_anonymizer.nix @@ -1,94 +1,111 @@ -import ./make-test-python.nix ({ pkgs, lib, ... }: { - name = "pg_anonymizer"; - meta.maintainers = lib.teams.flyingcircus.members; - - nodes.machine = { pkgs, ... }: { - environment.systemPackages = [ pkgs.pg-dump-anon ]; - services.postgresql = { - enable = true; - extraPlugins = ps: [ ps.anonymizer ]; - settings.shared_preload_libraries = [ "anon" ]; +{ system ? builtins.currentSystem +, config ? {} +, pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; + +let + inherit (pkgs) lib; + + makeAnonymizerTest = postgresqlPackage: + makeTest { + name = "pg_anonymizer-${postgresqlPackage.name}"; + meta.maintainers = lib.teams.flyingcircus.members; + + nodes.machine = { pkgs, ... }: { + environment.systemPackages = [ pkgs.pg-dump-anon ]; + services.postgresql = { + enable = true; + package = postgresqlPackage; + extraPlugins = ps: [ ps.anonymizer ]; + settings.shared_preload_libraries = [ "anon" ]; + }; + }; + + testScript = '' + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("postgresql.service") + + with subtest("Setup"): + machine.succeed("sudo -u postgres psql --command 'create database demo'") + machine.succeed( + "sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" '' + create extension anon cascade; + select anon.init(); + create table player(id serial, name text, points int); + insert into player(id,name,points) values (1,'Foo', 23); + insert into player(id,name,points) values (2,'Bar',42); + security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name();'; + security label for anon on column player.points is 'MASKED WITH VALUE NULL'; + ''}" + ) + + def get_player_table_contents(): + return [ + x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:] + ] + + def check_anonymized_row(row, id, original_name): + assert row[0] == id, f"Expected first row to have ID {id}, but got {row[0]}" + assert row[1] != original_name, f"Expected first row to have a name other than {original_name}" + assert not bool(row[2]), "Expected points to be NULL in first row" + + def find_xsv_in_dump(dump, sep=','): + """ + Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like + + COPY public.player ... + 1,Shields, + 2,Salazar, + \. + + in the given dump (the commas are tabs in case of pg_dump). + Extract the CSV lines and split by `sep`. + """ + + try: + from itertools import dropwhile, takewhile + return [x.split(sep) for x in list(takewhile( + lambda x: x != "\\.", + dropwhile( + lambda x: not x.startswith("COPY public.player"), + dump.splitlines() + ) + ))[1:]] + except: + print(f"Dump to process: {dump}") + raise + + def check_original_data(output): + assert output[0] == ['1','Foo','23'], f"Expected first row from player table to be 1,Foo,23; got {output[0]}" + assert output[1] == ['2','Bar','42'], f"Expected first row from player table to be 2,Bar,42; got {output[1]}" + + def check_anonymized_rows(output): + check_anonymized_row(output[0], '1', 'Foo') + check_anonymized_row(output[1], '2', 'Bar') + + with subtest("Check initial state"): + check_original_data(get_player_table_contents()) + + with subtest("Anonymous dumps"): + check_original_data(find_xsv_in_dump( + machine.succeed("sudo -u postgres pg_dump demo"), + sep='\t' + )) + check_anonymized_rows(find_xsv_in_dump( + machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"), + sep=',' + )) + + with subtest("Anonymize"): + machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'") + check_anonymized_rows(get_player_table_contents()) + ''; }; - }; - - testScript = '' - start_all() - machine.wait_for_unit("multi-user.target") - machine.wait_for_unit("postgresql.service") - - with subtest("Setup"): - machine.succeed("sudo -u postgres psql --command 'create database demo'") - machine.succeed( - "sudo -u postgres psql -d demo -f ${pkgs.writeText "init.sql" '' - create extension anon cascade; - select anon.init(); - create table player(id serial, name text, points int); - insert into player(id,name,points) values (1,'Foo', 23); - insert into player(id,name,points) values (2,'Bar',42); - security label for anon on column player.name is 'MASKED WITH FUNCTION anon.fake_last_name();'; - security label for anon on column player.points is 'MASKED WITH VALUE NULL'; - ''}" - ) - - def get_player_table_contents(): - return [ - x.split(',') for x in machine.succeed("sudo -u postgres psql -d demo --csv --command 'select * from player'").splitlines()[1:] - ] - - def check_anonymized_row(row, id, original_name): - assert row[0] == id, f"Expected first row to have ID {id}, but got {row[0]}" - assert row[1] != original_name, f"Expected first row to have a name other than {original_name}" - assert not bool(row[2]), "Expected points to be NULL in first row" - - def find_xsv_in_dump(dump, sep=','): - """ - Expecting to find a CSV (for pg_dump_anon) or TSV (for pg_dump) structure, looking like - - COPY public.player ... - 1,Shields, - 2,Salazar, - \. - - in the given dump (the commas are tabs in case of pg_dump). - Extract the CSV lines and split by `sep`. - """ - - try: - from itertools import dropwhile, takewhile - return [x.split(sep) for x in list(takewhile( - lambda x: x != "\\.", - dropwhile( - lambda x: not x.startswith("COPY public.player"), - dump.splitlines() - ) - ))[1:]] - except: - print(f"Dump to process: {dump}") - raise - - def check_original_data(output): - assert output[0] == ['1','Foo','23'], f"Expected first row from player table to be 1,Foo,23; got {output[0]}" - assert output[1] == ['2','Bar','42'], f"Expected first row from player table to be 2,Bar,42; got {output[1]}" - - def check_anonymized_rows(output): - check_anonymized_row(output[0], '1', 'Foo') - check_anonymized_row(output[1], '2', 'Bar') - - with subtest("Check initial state"): - check_original_data(get_player_table_contents()) - - with subtest("Anonymous dumps"): - check_original_data(find_xsv_in_dump( - machine.succeed("sudo -u postgres pg_dump demo"), - sep='\t' - )) - check_anonymized_rows(find_xsv_in_dump( - machine.succeed("sudo -u postgres pg_dump_anon -U postgres -h /run/postgresql -d demo"), - sep=',' - )) - - with subtest("Anonymize"): - machine.succeed("sudo -u postgres psql -d demo --command 'select anon.anonymize_database();'") - check_anonymized_rows(get_player_table_contents()) - ''; -}) +in +pkgs.lib.concatMapAttrs (n: p: { ${n} = makeAnonymizerTest p; }) pkgs.postgresqlVersions +// { + passthru.override = p: makeAnonymizerTest p; +} diff --git a/pkgs/servers/sql/postgresql/ext/anonymizer.nix b/pkgs/servers/sql/postgresql/ext/anonymizer.nix index 4bb5aa544440..559f7db3f675 100644 --- a/pkgs/servers/sql/postgresql/ext/anonymizer.nix +++ b/pkgs/servers/sql/postgresql/ext/anonymizer.nix @@ -1,9 +1,9 @@ -{ lib, stdenv, pg-dump-anon, postgresql, runtimeShell, jitSupport, llvm }: +{ lib, stdenv, pg-dump-anon, postgresql, runtimeShell, jitSupport, llvm, nixosTests }: stdenv.mkDerivation (finalAttrs: { pname = "postgresql_anonymizer"; - inherit (pg-dump-anon) version src passthru; + inherit (pg-dump-anon) version src; buildInputs = [ postgresql ]; nativeBuildInputs = [ postgresql ] ++ lib.optional jitSupport llvm; @@ -26,6 +26,8 @@ stdenv.mkDerivation (finalAttrs: { EOF ''; + passthru.tests = nixosTests.pg_anonymizer.passthru.override postgresql; + meta = lib.getAttrs [ "homepage" "maintainers" "license" ] pg-dump-anon.meta // { description = "Extension to mask or replace personally identifiable information (PII) or commercially sensitive data from a PostgreSQL database"; }; |