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
+59 -53
View File
@@ -11,88 +11,98 @@ use Symfony\Component\Form\FormFactoryInterface;
class UserTypeTest extends KernelTestCase
{
public function testRendersRolesWithoutSynthesizedRoles(): void
/**
* The form is the account settings and nothing else. Roles and hotel codes are synced
* from BusPro and editable nowhere, so they are not fields at all - a disabled field
* that cannot be submitted only reads as broken.
*/
public function testRolesAndHotelCodesAreNotFields(): void
{
$user = (new User())
->setRoles(['ROLE_ADMIN'])
->setHotelCodes(['DKS'])
;
$form = $this->createForm($user);
$this->assertFalse($form->has('roles'));
$this->assertFalse($form->has('hotelCodes'));
$this->assertFalse($form->has('approvedRoles'));
}
public function testNeitherRolesNorHotelCodesCanBeSubmitted(): void
{
$user = (new User())
->setRoles(['ROLE_TEAMER'])
->setHotelCodes(['SSL'])
;
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_ADMIN'],
'hotelCodes' => ['DKS'],
'disabled' => null,
]);
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame(['SSL'], $user->getHotelCodes());
}
/**
* Super admin is an elevation of ROLE_ADMIN, so it is not on offer for anybody else -
* offering it would only ever produce the violation from User::validateSuperAdmin().
*/
public function testSuperAdminIsOnlyOfferedToAnAdmin(): void
{
$this->assertTrue($this->createForm((new User())->setRoles(['ROLE_ADMIN']))->has('superAdmin'));
$this->assertFalse($this->createForm((new User())->setRoles(['ROLE_MANAGER']))->has('superAdmin'));
$this->assertFalse($this->createForm((new User())->setRoles([User::PENDING_ROLES['ROLE_ADMIN']]))->has('superAdmin'));
}
/**
* The exception: a flag left over from before its role was revoked has to stay editable,
* or that account could not be saved at all while the violation stands.
*/
public function testSuperAdminStaysEditableWhenTheFlagOutlivedTheRole(): void
{
$user = (new User())
->setRoles(['ROLE_MANAGER'])
->setSuperAdmin(true)
;
$view = $this->createForm($user)->createView();
// ROLE_USER and ROLE_SUPER_ADMIN are synthesized by getRoles() and must not leak in
$this->assertSame(['ROLE_ADMIN'], $view->children['roles']->vars['data']);
}
public function testApprovingAPendingRoleClearsTheMarker(): void
{
$user = (new User())->setRoles([User::PENDING_ROLES['ROLE_ADMIN']]);
$form = $this->createForm($user);
// the marker is not an assignable choice and must not reach the field
$this->assertSame([], $form->createView()->children['roles']->vars['data']);
$this->assertTrue($form->has('superAdmin'));
$form->submit([
'roles' => ['ROLE_ADMIN'],
'superAdmin' => null,
'hotelCodes' => [],
'disabled' => null,
]);
$this->assertTrue($form->isValid());
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame([], $user->getPendingRoles());
$this->assertFalse($user->isSuperAdmin());
}
public function testRendersHotelCodesNotCoveredByTheConfiguredMap(): void
public function testSuperAdminIsAppointed(): void
{
$user = (new User())->setHotelCodes(['XYZ']);
$view = $this->createForm($user)->createView();
$this->assertSame(['XYZ'], $view->children['hotelCodes']->vars['data']);
}
public function testSubmitStoresAssignedRolesOnly(): void
{
$user = (new User())->setRoles(['ROLE_TEAMER']);
$user = (new User())->setRoles(['ROLE_ADMIN']);
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_ADMIN'],
'superAdmin' => '1',
'hotelCodes' => [],
'disabled' => null,
]);
$this->assertTrue($form->isValid());
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertTrue($user->isSuperAdmin());
}
public function testSubmitRejectsSuperAdminWithoutRoleAdmin(): void
{
$user = new User();
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_MANAGER'],
'superAdmin' => '1',
'hotelCodes' => [],
]);
$this->assertFalse($form->isValid());
$this->assertCount(1, $form->get('superAdmin')->getErrors());
}
public function testSubmitBlocksTheAccountWithAReason(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_ADMIN'],
'superAdmin' => null,
'hotelCodes' => [],
'disabled' => '1',
'disabledReason' => 'Wegen Fehlverhaltens gesperrt.',
'disabledReasonInternal' => 'Siehe Vorgang 4711.',
@@ -115,9 +125,7 @@ class UserTypeTest extends KernelTestCase
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_ADMIN'],
'superAdmin' => null,
'hotelCodes' => [],
'disabled' => null,
'disabledReason' => null,
'disabledReasonInternal' => null,
@@ -143,9 +151,7 @@ class UserTypeTest extends KernelTestCase
// the textareas are prefilled, so unchecking the box alone submits the old reasons
$form->submit([
'roles' => ['ROLE_ADMIN'],
'superAdmin' => null,
'hotelCodes' => [],
'disabled' => null,
'disabledReason' => 'Für deinen Account liegt in BusPro keine Berechtigung mehr vor.',
'disabledReasonInternal' => 'Automatisch gesperrt: keine Rollen in BusPro.',