feat: align role assignment logic with myep-team

This commit is contained in:
Björn Fromme
2026-08-19 12:14:09 +02:00
parent 5a3957e143
commit 0c667d6b69
23 changed files with 1109 additions and 527 deletions
@@ -7,8 +7,21 @@ use App\BusProNet\Model\CrmAttributes;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\Traits\TypeConversionTrait;
use App\Security\Role;
use Symfony\Component\DomCrawler\Crawler;
/**
* Turns the SelektionCRM response into the roles and hotel codes the CRM claims for an account.
*
* This reports what BusPro says and nothing more: no fallback role, no default hotel code. What
* is made of the claims — which are granted outright and which only nominate — is Role::sync()'s
* business.
*
* The selection ids below are deployment-critical. BusPro always returns the full attribute tree
* and expresses membership through the `auswahl` flag, so a wrong or unset id yields a perfectly
* well-formed response in which nobody holds anything, and every user logging in is demoted one
* at a time. There is no signal inside the response that tells that apart from a real revocation.
*/
class CrmAttributesResponseParser
{
use TypeConversionTrait;
@@ -18,7 +31,13 @@ class CrmAttributesResponseParser
private const BPN_CRM_ID_TEAMER = 1070;
private const BPN_CRM_ID_GROUPS_MANAGER = 1477;
private const BPN_CRM_ID_GROUPS_ADMIN = 1478;
private const BPN_DEFAULT_HOTEL_CODE = 'SSL';
/**
* @param array<int|string, string> $houseManagerIds "Hausleitung" selection id => hotel code
*/
public function __construct(private readonly array $houseManagerIds = [])
{
}
public function parse(Crawler $result): CrmAttributes
{
@@ -45,24 +64,28 @@ class CrmAttributesResponseParser
$attribute->mutable = $this->stringToBool($node->attr('aenderbar'));
$attribute->selected = $this->stringToBool($node->attr('auswahl'));
if (1 === preg_match('/^Hausleitung ([A-Z0-9]+)$/', $attribute->label, $matches) && true === $attribute->selected) {
$roles[] = 'ROLE_HOUSE_MANAGER';
$hotelCodes[] = $matches[1];
}
if (self::BPN_CRM_ID_ADMIN === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_ADMIN';
}
if (self::BPN_CRM_ID_MANAGER === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_MANAGER';
}
if (self::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_TEAMER';
}
if (self::BPN_CRM_ID_GROUPS_MANAGER === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_GROUPS_MANAGER';
}
if (self::BPN_CRM_ID_GROUPS_ADMIN === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_GROUPS_ADMIN';
if (true === $attribute->selected) {
$hotelCode = $this->houseManagerIds[$attribute->id] ?? null;
if (null !== $hotelCode) {
$roles[] = Role::HOUSE_MANAGER;
$hotelCodes[] = $hotelCode;
}
if (self::BPN_CRM_ID_ADMIN === $attribute->id) {
$roles[] = Role::ADMIN;
}
if (self::BPN_CRM_ID_MANAGER === $attribute->id) {
$roles[] = Role::MANAGER;
}
if (self::BPN_CRM_ID_TEAMER === $attribute->id) {
$roles[] = Role::TEAMER;
}
if (self::BPN_CRM_ID_GROUPS_MANAGER === $attribute->id) {
$roles[] = Role::GROUPS_MANAGER;
}
if (self::BPN_CRM_ID_GROUPS_ADMIN === $attribute->id) {
$roles[] = Role::GROUPS_ADMIN;
}
}
$attributes[] = $attribute;
@@ -74,13 +97,6 @@ class CrmAttributesResponseParser
})
;
$roles = array_unique($roles);
// Assign default role if none could be resolved
if (0 === count($roles)) {
$roles = ['ROLE_CUSTOMER'];
}
$result
->filterXPath('//crmaktionen/crmaktion')
->each(function (Crawler $node) use (&$actions) {
@@ -96,15 +112,11 @@ class CrmAttributesResponseParser
})
;
if (true === in_array('ROLE_ADMIN', $roles, true)) {
$hotelCodes[] = self::BPN_DEFAULT_HOTEL_CODE;
}
$response = new CrmAttributes();
$response->selectionGroups = $groups;
$response->crmActions = $actions;
$response->roles = $roles;
$response->hotelCodes = $hotelCodes;
$response->roles = array_values(array_unique($roles));
$response->hotelCodes = array_values(array_unique($hotelCodes));
return $response;
}