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
+19 -1
View File
@@ -112,12 +112,16 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
): ?User {
// Fetch CRM attributes, early return in case of an API error
try {
/** @var CrmAttributesResponse $crmAttributes */
$crmAttributes = $this->apiClient->getCrmAttributes($email, $password);
} catch (ApiClientException $e) {
return null;
}
// BusPro answers with a notification record instead of the data on its own errors
if (false === $crmAttributes instanceof CrmAttributesResponse) {
return null;
}
// Flatten selected CRM attributes
$crmSelections = $crmAttributes->toArray();
@@ -140,6 +144,20 @@ class BpnAuthenticator extends AbstractLoginFormAuthenticator implements Authent
// are no user of it: never create an account, block an existing one. Returning
// the blocked user lets the UserChecker explain why the login was refused.
if ([] === $claimedRoles) {
// A response without any attribute group carries no roles either, so it looks
// exactly like a revocation while it really means the CRM told us nothing:
// an empty payload, a changed schema, a misconfigured attribute id. Blocking
// on that would lock out every user logging in, so refuse this single login
// instead and leave the account alone.
if ([] === ($crmAttributes->getAttributeGroups() ?? [])) {
$this->logger->warning('Skip demotion: CRM attributes response carries no attribute groups', [
'user_id' => $user?->getId(),
'user_email' => $email,
]);
return null;
}
if (null === $user) {
return null;
}