fix: drop the implicit ROLE_USER from the userinfo roles claim

This commit is contained in:
Björn Fromme
2026-08-12 17:47:44 +02:00
parent aa486a4931
commit d74127ebe2
4 changed files with 28 additions and 8 deletions
+4 -2
View File
@@ -8,6 +8,7 @@ use App\BusProNet\Model\Notification;
use App\BusProNet\Model\PersonalData;
use App\Entity\User;
use App\Security\Crypt;
use App\Security\Role;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@@ -52,8 +53,9 @@ class UserinfoController extends AbstractController
return new JsonResponse(['message' => $data->message, 'code' => $data->code], Response::HTTP_BAD_REQUEST);
}
// Patch current user's roles
$data->roles = $user->getRoles();
// Patch current user's roles. The implicit ROLE_USER says nothing about the
// account — every authenticated user holds it — and is not exported.
$data->roles = Role::assignedOnly($user->getRoles());
// Patch current user's hotel codes
$data->hotelCodes = $user->getHotelCodes();
+14 -1
View File
@@ -72,6 +72,19 @@ final class Role
return [] === $importable ? [self::CUSTOMER] : $importable;
}
/**
* The roles actually assigned to an account — everything except the implicit ROLE_USER,
* which User::getRoles() prepends and which is never stored.
*
* @param string[] $roles
*
* @return string[]
*/
public static function assignedOnly(array $roles): array
{
return array_values(array_diff($roles, [self::USER]));
}
/**
* The non-privileged half of a role set: what BpnAuthenticator syncs from the BusPro CRM.
*
@@ -81,7 +94,7 @@ final class Role
*/
public static function syncedOnly(array $roles): array
{
return array_values(array_diff($roles, self::PRIVILEGED, [self::USER]));
return array_values(array_diff(self::assignedOnly($roles), self::PRIVILEGED));
}
/**
+1 -5
View File
@@ -141,11 +141,7 @@ class AppRuntime implements RuntimeExtensionInterface
$mapped = [];
foreach ($roles as $role) {
if ('ROLE_USER' === $role) {
continue;
}
foreach (Role::assignedOnly($roles) as $role) {
$mapped[] = $labels[$role] ?? $role;
}
+9
View File
@@ -38,6 +38,15 @@ class RoleTest extends TestCase
self::assertSame([Role::CUSTOMER], Role::filterImportable([]));
}
public function testAssignedOnlyDropsTheImplicitRoleUser(): void
{
$roles = Role::assignedOnly([Role::USER, Role::TEAMER, Role::GROUPS_ADMIN]);
self::assertSame([Role::TEAMER, Role::GROUPS_ADMIN], $roles);
// The value is JSON-encoded into the userinfo response and must not become an object.
self::assertSame(array_keys($roles), range(0, \count($roles) - 1));
}
public function testCombineKeepsEachHalfInItsOwnLane(): void
{
$roles = Role::combine([Role::TEAMER, Role::ADMIN], [Role::GROUPS_ADMIN, Role::TEAMER]);