feat: bpn as single source of truth for role and hotel code assignments

This commit is contained in:
Björn Fromme
2026-08-18 11:33:42 +02:00
parent 1fd0fbc21e
commit f0e850978b
17 changed files with 818 additions and 198 deletions
+13 -5
View File
@@ -128,8 +128,15 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
// Determine teamer status from CRM attributes
$isTeamer = $crmAttributes->isTeamer();
// Everything the CRM grants this person here: pending markers plus ROLE_TEAMER
// What the CRM claims, as plain role names, and what this application makes of it:
// pending markers plus ROLE_TEAMER. The first drives the sync, the second is what
// a new account starts with.
$claimedRoles = $this
->userDataHandler
->collectClaimedRoles($crmAttributes)
;
$grantedRoles = $this
->userDataHandler
->collectRoles($crmAttributes)
;
@@ -180,8 +187,8 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
return $user;
}
// Update existing user's teamer data and return it, leaving roles and hotel
// codes alone: they are imported once on creation and managed manually after
// Update the existing user, re-syncing roles and hotel codes from the CRM: it
// leads, so a role or a house it no longer reports is withdrawn here
if (null !== $user) {
$this
->userDataHandler
@@ -190,7 +197,8 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
$profileResponse,
$isTeamer,
$crmSelections,
$this->userDataHandler->collectPendingRoles($crmAttributes),
$claimedRoles,
$crmAttributes->getHotelCodes(),
)
;
@@ -202,7 +210,7 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
->userDataHandler
->createLocalUser(
$profileResponse,
$claimedRoles,
$grantedRoles,
$isTeamer,
$crmSelections,
$crmAttributes->getHotelCodes(),
+22 -11
View File
@@ -161,6 +161,7 @@ class MyEpAuthenticator extends AbstractAuthenticator
$isTeamer = in_array('ROLE_TEAMER', $claimedRoles, true);
$pendingRoles = $this->userDataHandler->toPendingRoles($claimedRoles);
$hotelCodes = $this->collectHotelCodes($userinfo);
$user = $this->findLocalUser($userinfo);
@@ -177,23 +178,20 @@ class MyEpAuthenticator extends AbstractAuthenticator
return $user;
}
// Update existing user, leaving granted roles and hotel codes alone: they are
// imported once on creation and managed manually afterwards. Only the
// privilege-free markers and ROLE_TEAMER track MyE&P on every login.
// Update existing user. MyE&P leads here exactly as BusPro does on the other login
// path - the shared policy in UserDataHandler withdraws what is no longer claimed
// and marks administrative claims for approval rather than granting them.
if (null !== $user) {
$this->refreshBusProIds($user, $userinfo);
$this->userDataHandler->refreshPendingRoles($user, $pendingRoles);
if (true === $isTeamer) {
$this->userDataHandler->grantTeamerRole($user);
}
$this->userDataHandler->syncRoles($user, $claimedRoles);
$this->userDataHandler->syncHotelCodes($user, $hotelCodes);
$this->entityManager->flush();
return $user;
}
return $this->createLocalUser($userinfo, $pendingRoles, $isTeamer);
return $this->createLocalUser($userinfo, $pendingRoles, $isTeamer, $hotelCodes);
}
/**
@@ -233,6 +231,19 @@ class MyEpAuthenticator extends AbstractAuthenticator
return $claimedRoles;
}
/**
* The houses MyE&P reports for this person. Read on both the create and the update
* path, since they are led by the identity provider just like the roles are.
*
* @return string[]
*/
private function collectHotelCodes(array $userinfo): array
{
$profile = is_array($userinfo['profile'] ?? null) ? $userinfo['profile'] : [];
return is_array($profile['hotel_codes'] ?? null) ? array_values($profile['hotel_codes']) : [];
}
/**
* Matches the local account on the BusPro ids first and falls back to the email, the
* same precedence UserDataHandler::findLocalUser() applies to a BusPro login, so that
@@ -300,8 +311,9 @@ class MyEpAuthenticator extends AbstractAuthenticator
/**
* @param string[] $pendingRoles
* @param string[] $hotelCodes
*/
private function createLocalUser(array $userinfo, array $pendingRoles, bool $isTeamer): ?User
private function createLocalUser(array $userinfo, array $pendingRoles, bool $isTeamer, array $hotelCodes): ?User
{
$addressId = $userinfo['address_id'] ?? null;
$personId = $userinfo['person_id'] ?? null;
@@ -318,7 +330,6 @@ class MyEpAuthenticator extends AbstractAuthenticator
}
$profile = is_array($userinfo['profile'] ?? null) ? $userinfo['profile'] : [];
$hotelCodes = is_array($profile['hotel_codes'] ?? null) ? $profile['hotel_codes'] : [];
$user = new User();
$user