feat: full anonymization of buspro xml dumps

This commit is contained in:
Björn Fromme
2026-06-21 16:49:54 +02:00
parent d77f92b6f0
commit 52ee6b26b1
6 changed files with 922 additions and 85 deletions
+288 -63
View File
@@ -15,41 +15,41 @@ class BpnXmlAnonymizerTest extends TestCase
<?xml version="1.0" encoding="UTF-8"?>
<anfrage>
<anmelder>
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Example</name>
<vorname>Ava</vorname>
<anschrift>
<strasse>Am Jägersteig 19</strasse>
<plz>40724</plz>
<ort>Hilden</ort>
<strasse>Testallee 7</strasse>
<plz>10115</plz>
<ort>Berlin</ort>
</anschrift>
<kommunikation>
<email>[email protected]</email>
<telefonmobil>02212722760</telefonmobil>
<email>[email protected]</email>
<telefonmobil>015500000001</telefonmobil>
</kommunikation>
<idadresseperson>255752</idadresseperson>
<idadresseperson>111111</idadresseperson>
</anmelder>
<teilnehmerliste>
<teilnehmer id="1">
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Example</name>
<vorname>Ava</vorname>
<anschrift>
<strasse>Am Jägersteig 19</strasse>
<plz>40724</plz>
<ort>Hilden</ort>
<strasse>Testallee 7</strasse>
<plz>10115</plz>
<ort>Berlin</ort>
</anschrift>
<kommunikation>
<email>[email protected]</email>
<telefonmobil>02212722760</telefonmobil>
<email>[email protected]</email>
<telefonmobil>015500000001</telefonmobil>
</kommunikation>
<idadresseperson>255752</idadresseperson>
<idadresseperson>111111</idadresseperson>
</teilnehmer>
<teilnehmer id="2">
<name>Danielzik</name>
<vorname>Leon</vorname>
<name>Second</name>
<vorname>Ben</vorname>
<kommunikation>
<email>[email protected]</email>
<email>[email protected]</email>
</kommunikation>
<idadresseperson>306886</idadresseperson>
<idadresseperson>222222</idadresseperson>
</teilnehmer>
</teilnehmerliste>
</anfrage>
@@ -64,19 +64,141 @@ XML;
$xpath = new \DOMXPath($document);
$applicantName = trim((string) $xpath->evaluate('string(//anmelder/name)'));
$participantOneName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/name)'));
$participantOneEmail = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/kommunikation/email)'));
$participantOneName = trim((string) $xpath->evaluate('string(//teilnehmer[1]/name)'));
$participantOneEmail = trim((string) $xpath->evaluate('string(//teilnehmer[1]/kommunikation/email)'));
$applicantMobilePhone = trim((string) $xpath->evaluate('string(//anmelder/kommunikation/telefonmobil)'));
$participantOneMobilePhone = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/kommunikation/telefonmobil)'));
$participantTwoName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="2"]/name)'));
$participantOneMobilePhone = trim((string) $xpath->evaluate('string(//teilnehmer[1]/kommunikation/telefonmobil)'));
$participantTwoName = trim((string) $xpath->evaluate('string(//teilnehmer[2]/name)'));
$this->assertNotSame('Schlegelmilch', $applicantName);
$this->assertNotSame('Example', $applicantName);
$this->assertSame($applicantName, $participantOneName);
$this->assertStringEndsWith('@example.com', $participantOneEmail);
$this->assertNotSame('02212722760', $participantOneMobilePhone);
$this->assertSame('anon-user-2@example.com', $participantOneEmail);
$this->assertSame('01000000002', $participantOneMobilePhone);
$this->assertSame($applicantMobilePhone, $participantOneMobilePhone);
$this->assertNotSame($participantOneName, $participantTwoName);
$this->assertSame(2, $anonymizer->getLastIdentityCount());
$this->assertSame(3, $anonymizer->getLastIdentityCount());
}
public function testAnonymizeReplacesAdressdatenRequestAndResponseConsistently(): void
{
$requestXml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<anfrage>
<user>portal-user</user>
<key>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</key>
<satz typ="KUNDENKONTO"/>
<art>Adressdaten</art>
<email>[email protected]</email>
<passwort>bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</passwort>
</anfrage>
XML;
$responseXml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<ergebnis>
<satz typ="KUNDENKONTO"/>
<art>Adressdaten</art>
<idadresse>654321</idadresse>
<idperson>123456</idperson>
<adressdaten>
<name>Input</name>
<vorname>Person</vorname>
<anrede>Herr</anrede>
<titel>Prof.</titel>
<geschlecht>M</geschlecht>
<geburtsdatum>16.04.1972</geburtsdatum>
<nationalitaet>NL</nationalitaet>
<anschrift>
<id>444444</id>
<strasse>Beispielweg 57</strasse>
<plz>42853</plz>
<ort>Musterstadt</ort>
<ortsteil>Innenstadt</ortsteil>
<land>D</land>
</anschrift>
<kommunikation>
<telefonmobil>017700000000</telefonmobil>
<email>[email protected]</email>
<newsletter>False</newsletter>
<telefonprivat>021900000000</telefonprivat>
</kommunikation>
<bankverbindung>
<iban>DE02120300000000202051</iban>
</bankverbindung>
<sonstiges1>179-197</sonstiges1>
<sonstiges2>79-94</sonstiges2>
<sonstiges3>44</sonstiges3>
</adressdaten>
</ergebnis>
XML;
$anonymizer = new BpnXmlAnonymizer();
$anonymizedRequest = $anonymizer->anonymize($requestXml, true);
$anonymizedResponse = $anonymizer->anonymize($responseXml, false);
$requestDocument = new \DOMDocument();
$this->assertTrue($requestDocument->loadXML($anonymizedRequest));
$requestXpath = new \DOMXPath($requestDocument);
$requestUser = trim((string) $requestXpath->evaluate('string(//user)'));
$requestEmail = trim((string) $requestXpath->evaluate('string(//email)'));
$requestKey = trim((string) $requestXpath->evaluate('string(//key)'));
$requestPassword = trim((string) $requestXpath->evaluate('string(//passwort)'));
$responseDocument = new \DOMDocument();
$this->assertTrue($responseDocument->loadXML($anonymizedResponse));
$responseXpath = new \DOMXPath($responseDocument);
$responseEmail = trim((string) $responseXpath->evaluate('string(//adressdaten/kommunikation/email)'));
$responseName = trim((string) $responseXpath->evaluate('string(//adressdaten/name)'));
$responseFirstName = trim((string) $responseXpath->evaluate('string(//adressdaten/vorname)'));
$responseSalutation = trim((string) $responseXpath->evaluate('string(//adressdaten/anrede)'));
$responseTitle = trim((string) $responseXpath->evaluate('string(//adressdaten/titel)'));
$responseGender = trim((string) $responseXpath->evaluate('string(//adressdaten/geschlecht)'));
$responseBirthDate = trim((string) $responseXpath->evaluate('string(//adressdaten/geburtsdatum)'));
$responseNationality = trim((string) $responseXpath->evaluate('string(//adressdaten/nationalitaet)'));
$responseStreet = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/strasse)'));
$responsePostalCode = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/plz)'));
$responseCity = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/ort)'));
$responseDistrict = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/ortsteil)'));
$responseCountry = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/land)'));
$responseAddressId = trim((string) $responseXpath->evaluate('string(//adressdaten/anschrift/id)'));
$responseMobilePhone = trim((string) $responseXpath->evaluate('string(//adressdaten/kommunikation/telefonmobil)'));
$responsePhone = trim((string) $responseXpath->evaluate('string(//adressdaten/kommunikation/telefonprivat)'));
$responseNewsletter = trim((string) $responseXpath->evaluate('string(//adressdaten/kommunikation/newsletter)'));
$responseIban = trim((string) $responseXpath->evaluate('string(//adressdaten/bankverbindung/iban)'));
$responseHeight = trim((string) $responseXpath->evaluate('string(//adressdaten/sonstiges1)'));
$responseWeight = trim((string) $responseXpath->evaluate('string(//adressdaten/sonstiges2)'));
$responseShoeSize = trim((string) $responseXpath->evaluate('string(//adressdaten/sonstiges3)'));
$responseRootPersonId = trim((string) $responseXpath->evaluate('string(//idperson)'));
$responseRootAddressId = trim((string) $responseXpath->evaluate('string(//idadresse)'));
$this->assertSame('anon-user-1', $requestUser);
$this->assertSame('[email protected]', $requestEmail);
$this->assertSame('00000000000000000000000000000001', $requestKey);
$this->assertSame('00000000000000000000000000000002', $requestPassword);
$this->assertSame($requestEmail, $responseEmail);
$this->assertNotSame('Input', $responseName);
$this->assertNotSame('Person', $responseFirstName);
$this->assertSame('Divers', $responseSalutation);
$this->assertSame('', $responseTitle);
$this->assertSame('D', $responseGender);
$this->assertSame('01.01.1980', $responseBirthDate);
$this->assertSame('XX', $responseNationality);
$this->assertNotSame('Beispielweg 57', $responseStreet);
$this->assertNotSame('42853', $responsePostalCode);
$this->assertNotSame('Musterstadt', $responseCity);
$this->assertSame('', $responseDistrict);
$this->assertSame('XX', $responseCountry);
$this->assertSame('800001', $responseAddressId);
$this->assertSame('01000000001', $responseMobilePhone);
$this->assertSame('02000000001', $responsePhone);
$this->assertSame('False', $responseNewsletter);
$this->assertSame('DE00000000000000000001', $responseIban);
$this->assertSame('170', $responseHeight);
$this->assertSame('70', $responseWeight);
$this->assertSame('42', $responseShoeSize);
$this->assertSame('123456', $responseRootPersonId);
$this->assertSame('654321', $responseRootAddressId);
$this->assertSame(1, $anonymizer->getLastIdentityCount());
}
public function testAnonymizeReplacesKundeAttributes(): void
@@ -84,14 +206,14 @@ XML;
$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<ergebnis>
<vorgaenge>
<vorgang>
<kundennamen>
<kunde id="1" art="Anmelder/Teilnehmer" name="Fromme" vorname="Björn"></kunde>
<kunde id="2" art="Teilnehmer" name="Rütten" vorname="Manuela"></kunde>
</kundennamen>
</vorgang>
</vorgaenge>
<vorgaenge>
<vorgang>
<kundennamen>
<kunde id="1" art="Anmelder/Teilnehmer" name="Alpha" vorname="Ada"></kunde>
<kunde id="2" art="Teilnehmer" name="Beta" vorname="Ben"></kunde>
</kundennamen>
</vorgang>
</vorgaenge>
</ergebnis>
XML;
@@ -103,11 +225,15 @@ XML;
$xpath = new \DOMXPath($document);
$firstCustomerLastName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@name)'));
$firstCustomerFirstName = trim((string) $xpath->evaluate('string(//kunde[@id="1"]/@vorname)'));
$firstCustomerLastName = trim((string) $xpath->evaluate('string(//kundennamen/kunde[1]/@name)'));
$firstCustomerFirstName = trim((string) $xpath->evaluate('string(//kundennamen/kunde[1]/@vorname)'));
$firstCustomerId = trim((string) $xpath->evaluate('string(//kundennamen/kunde[1]/@id)'));
$secondCustomerId = trim((string) $xpath->evaluate('string(//kundennamen/kunde[2]/@id)'));
$this->assertNotSame('Fromme', $firstCustomerLastName);
$this->assertNotSame('Björn', $firstCustomerFirstName);
$this->assertNotSame('Alpha', $firstCustomerLastName);
$this->assertNotSame('Ada', $firstCustomerFirstName);
$this->assertSame('900001', $firstCustomerId);
$this->assertSame('900002', $secondCustomerId);
}
public function testAnonymizeThrowsOnInvalidXml(): void
@@ -125,28 +251,28 @@ XML;
<?xml version="1.0" encoding="UTF-8"?>
<anfrage>
<anmelder>
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Shared</name>
<vorname>Casey</vorname>
<anschrift>
<strasse>Am Jägersteig 19</strasse>
<plz>40724</plz>
<ort>Hilden</ort>
<strasse>Sharedstrasse 1</strasse>
<plz>50667</plz>
<ort>Koeln</ort>
</anschrift>
<kommunikation>
<email>[email protected]</email>
<email>[email protected]</email>
</kommunikation>
</anmelder>
<teilnehmerliste>
<teilnehmer id="1">
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Shared</name>
<vorname>Casey</vorname>
<anschrift>
<strasse>Am Jägersteig 19</strasse>
<plz>40724</plz>
<ort>Hilden</ort>
<strasse>Sharedstrasse 1</strasse>
<plz>50667</plz>
<ort>Koeln</ort>
</anschrift>
<kommunikation>
<email>[email protected]</email>
<email>[email protected]</email>
</kommunikation>
</teilnehmer>
</teilnehmerliste>
@@ -162,10 +288,10 @@ XML;
$xpath = new \DOMXPath($document);
$applicantName = trim((string) $xpath->evaluate('string(//anmelder/name)'));
$participantName = trim((string) $xpath->evaluate('string(//teilnehmer[@id="1"]/name)'));
$participantName = trim((string) $xpath->evaluate('string(//teilnehmer[1]/name)'));
$this->assertSame($applicantName, $participantName);
$this->assertSame(1, $anonymizer->getLastIdentityCount());
$this->assertSame(2, $anonymizer->getLastIdentityCount());
}
public function testAnonymizeCanReuseIdentityAcrossMultipleXmlDocuments(): void
@@ -174,12 +300,12 @@ XML;
<?xml version="1.0" encoding="UTF-8"?>
<anfrage>
<anmelder>
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Reusable</name>
<vorname>Riley</vorname>
<kommunikation>
<email>[email protected]</email>
<email>[email protected]</email>
</kommunikation>
<idadresseperson>255752</idadresseperson>
<idadresseperson>333333</idadresseperson>
</anmelder>
</anfrage>
XML;
@@ -189,12 +315,12 @@ XML;
<ergebnis>
<teilnehmerliste>
<teilnehmer id="1">
<name>Schlegelmilch</name>
<vorname>Mandy</vorname>
<name>Reusable</name>
<vorname>Riley</vorname>
<kommunikation>
<email>[email protected]</email>
<email>[email protected]</email>
</kommunikation>
<idadresseperson>255752</idadresseperson>
<idadresseperson>333333</idadresseperson>
</teilnehmer>
</teilnehmerliste>
</ergebnis>
@@ -212,9 +338,108 @@ XML;
$responseDocument = new \DOMDocument();
$this->assertTrue($responseDocument->loadXML($anonymizedResponse));
$responseXpath = new \DOMXPath($responseDocument);
$responseName = trim((string) $responseXpath->evaluate('string(//teilnehmer[@id="1"]/name)'));
$responseName = trim((string) $responseXpath->evaluate('string(//teilnehmer[1]/name)'));
$this->assertSame($requestName, $responseName);
$this->assertSame(2, $anonymizer->getLastIdentityCount());
}
public function testAnonymizeReplacesAdressdatenBlocksInCustomerResponses(): void
{
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<ergebnis>
<satz typ="KUNDENKONTO"/>
<art>Adressdaten</art>
<idadresse>654321</idadresse>
<idperson>123456</idperson>
<adressdaten>
<name>Input</name>
<vorname>Person</vorname>
<anrede>Frau</anrede>
<titel>Dr.</titel>
<geschlecht>W</geschlecht>
<geburtsdatum>01.02.1990</geburtsdatum>
<nationalitaet>DE</nationalitaet>
<anschrift>
<id>444444</id>
<strasse>Beispielweg 12</strasse>
<plz>12345</plz>
<ort>Musterstadt</ort>
<ortsteil>Altstadt</ortsteil>
<land>DE</land>
</anschrift>
<kommunikation>
<telefonmobil>01701234567</telefonmobil>
<email>[email protected]</email>
<newsletter>True</newsletter>
<telefonprivat>02101234567</telefonprivat>
</kommunikation>
<bankverbindung>
<iban>DE02120300000000202051</iban>
</bankverbindung>
<sonstiges1>180</sonstiges1>
<sonstiges2>75</sonstiges2>
<sonstiges3>43</sonstiges3>
</adressdaten>
</ergebnis>
XML;
$anonymizer = new BpnXmlAnonymizer();
$result = $anonymizer->anonymize($xml);
$document = new \DOMDocument();
$this->assertTrue($document->loadXML($result));
$xpath = new \DOMXPath($document);
$lastName = trim((string) $xpath->evaluate('string(//adressdaten/name)'));
$firstName = trim((string) $xpath->evaluate('string(//adressdaten/vorname)'));
$salutation = trim((string) $xpath->evaluate('string(//adressdaten/anrede)'));
$title = trim((string) $xpath->evaluate('string(//adressdaten/titel)'));
$gender = trim((string) $xpath->evaluate('string(//adressdaten/geschlecht)'));
$birthDate = trim((string) $xpath->evaluate('string(//adressdaten/geburtsdatum)'));
$nationality = trim((string) $xpath->evaluate('string(//adressdaten/nationalitaet)'));
$street = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/strasse)'));
$postalCode = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/plz)'));
$city = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/ort)'));
$district = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/ortsteil)'));
$country = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/land)'));
$addressId = trim((string) $xpath->evaluate('string(//adressdaten/anschrift/id)'));
$email = trim((string) $xpath->evaluate('string(//adressdaten/kommunikation/email)'));
$mobilePhone = trim((string) $xpath->evaluate('string(//adressdaten/kommunikation/telefonmobil)'));
$phone = trim((string) $xpath->evaluate('string(//adressdaten/kommunikation/telefonprivat)'));
$newsletter = trim((string) $xpath->evaluate('string(//adressdaten/kommunikation/newsletter)'));
$iban = trim((string) $xpath->evaluate('string(//adressdaten/bankverbindung/iban)'));
$height = trim((string) $xpath->evaluate('string(//adressdaten/sonstiges1)'));
$weight = trim((string) $xpath->evaluate('string(//adressdaten/sonstiges2)'));
$shoeSize = trim((string) $xpath->evaluate('string(//adressdaten/sonstiges3)'));
$personId = trim((string) $xpath->evaluate('string(//idperson)'));
$addressIdRoot = trim((string) $xpath->evaluate('string(//idadresse)'));
$this->assertNotSame('Input', $lastName);
$this->assertNotSame('Person', $firstName);
$this->assertSame('Divers', $salutation);
$this->assertSame('', $title);
$this->assertSame('D', $gender);
$this->assertSame('01.01.1980', $birthDate);
$this->assertSame('XX', $nationality);
$this->assertNotSame('Beispielweg 12', $street);
$this->assertNotSame('12345', $postalCode);
$this->assertNotSame('Musterstadt', $city);
$this->assertSame('', $district);
$this->assertSame('XX', $country);
$this->assertSame('800001', $addressId);
$this->assertSame('[email protected]', $email);
$this->assertSame('01000000001', $mobilePhone);
$this->assertSame('02000000001', $phone);
$this->assertSame('False', $newsletter);
$this->assertSame('DE00000000000000000001', $iban);
$this->assertSame('170', $height);
$this->assertSame('70', $weight);
$this->assertSame('42', $shoeSize);
$this->assertSame('123456', $personId);
$this->assertSame('654321', $addressIdRoot);
$this->assertSame(1, $anonymizer->getLastIdentityCount());
}
}