feat: streamlined role-revocation logic

This commit is contained in:
Björn Fromme
2026-08-10 16:24:41 +02:00
parent b39a78da82
commit fd5d478a5c
14 changed files with 426 additions and 22 deletions
+58
View File
@@ -48,6 +48,59 @@ class ResponseParserTest extends TestCase
$this->assertTrue($response->isTeamer());
}
/**
* BusPro always returns the full attribute tree and expresses the roles a person holds
* through the "auswahl" flag, so a revoked role arrives as a selection set to False,
* never as a missing group. The demotion path in BpnAuthenticator relies on that: it
* treats "no roles" as a revocation only when attribute groups are present.
*/
public function testParseCrmAttributesOfAUserHoldingEveryRole(): void
{
$parser = $this->getParserInstance();
$response = $parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, $this->loadFixture('crm_attributes_granted.xml'));
$this->assertInstanceOf(CrmAttributesResponse::class, $response);
$this->assertTrue($response->isTeamer());
$this->assertTrue($response->isAdmin());
$this->assertTrue($response->isManager());
$this->assertTrue($response->isHouseManager());
$this->assertSame(['DKS'], $response->getHotelCodes());
// "Preisrechner Admin" is matched by id, never by its label
$this->assertCount(3, $response->getAttributeGroups());
}
public function testParseCrmAttributesOfAUserWhoseRolesWereRevoked(): void
{
$parser = $this->getParserInstance();
$response = $parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, $this->loadFixture('crm_attributes_revoked.xml'));
$this->assertInstanceOf(CrmAttributesResponse::class, $response);
$this->assertFalse($response->isTeamer());
$this->assertFalse($response->isAdmin());
$this->assertFalse($response->isManager());
$this->assertFalse($response->isHouseManager());
$this->assertSame([], $response->getHotelCodes());
// the groups still arrive, so this is a revocation and not a degraded response
$this->assertCount(3, $response->getAttributeGroups());
}
public function testParseCrmAttributesOfAnEmptyResponse(): void
{
$content = '<?xml version="1.0" encoding="utf-8"?><ergebnis><satz typ="KUNDENKONTO"></satz><art>SelektionCRM</art><idadresse>141747</idadresse><idperson>224526</idperson><selektionsmerkmale></selektionsmerkmale></ergebnis>';
$parser = $this->getParserInstance();
$response = $parser->parseXmlString(ApiClient::TYPE_CUSTOMER_DATA, $content);
// indistinguishable from a revocation by the roles alone, which is why the empty
// group set is what the demotion path checks
$this->assertInstanceOf(CrmAttributesResponse::class, $response);
$this->assertFalse($response->isTeamer());
$this->assertSame([], $response->getAttributeGroups());
$this->assertSame([], $response->toArray());
}
public function testParseSuccessfulProfileUpdateResponseWithoutAddressData(): void
{
$content = '<?xml version="1.0" encoding="utf-8"?><ergebnis><satz typ="KUNDENKONTO"></satz><art>Adressdaten_Ändern</art><idadresse>141747</idadresse><idperson>224526</idperson><änderung>True</änderung></ergebnis>';
@@ -106,6 +159,11 @@ class ResponseParserTest extends TestCase
$this->assertEquals('Gaststätte', $hotel->getType());
}
private function loadFixture(string $filename): string
{
return file_get_contents(__DIR__.'/../Resources/'.$filename);
}
private function getParserInstance(): ResponseParser
{
return new ResponseParser([