feat: rename ROLE_ADMIN to ROLE_TEAM_ADMIN since being too broad

This commit is contained in:
2026-09-16 17:20:45 +02:00
parent 39a343911a
commit d4908b6ab9
75 changed files with 212 additions and 180 deletions
+1 -1
View File
@@ -55,7 +55,7 @@ APP_BPN_DEBUG=false
# This hotel code will be assigned to admin users together with ROLE_HOTEL_MANAGER
# in dev and staging environments for testing purposes
APP_BPN_DEFAULT_HOTEL_CODE=
APP_BPN_CRM_ID_ADMIN=1292
APP_BPN_CRM_ID_TEAM_ADMIN=1484
APP_BPN_CRM_ID_MANAGER=1293
APP_BPN_CRM_ID_TEAMER=1070
+1 -1
View File
@@ -9,7 +9,7 @@ security:
property: email
role_hierarchy:
ROLE_ADMIN: [ ROLE_ADMINISTRATIVE ]
ROLE_TEAM_ADMIN: [ ROLE_ADMINISTRATIVE ]
ROLE_MANAGER: [ ROLE_ADMINISTRATIVE ]
firewalls:
+2 -2
View File
@@ -1,6 +1,6 @@
# yaml-language-server: $schema=../vendor/symfony/dependency-injection/Loader/schema/services.schema.json
parameters:
bpn_crm_id_admin: '%env(int:APP_BPN_CRM_ID_ADMIN)%'
bpn_crm_id_team_admin: '%env(int:APP_BPN_CRM_ID_TEAM_ADMIN)%'
bpn_crm_id_manager: '%env(int:APP_BPN_CRM_ID_MANAGER)%'
bpn_crm_id_teamer: '%env(int:APP_BPN_CRM_ID_TEAMER)%'
bpn_default_hotel_code: '%env(default::APP_BPN_DEFAULT_HOTEL_CODE)%'
@@ -162,7 +162,7 @@ services:
App\BusProNet\ResponseParser:
arguments:
$options:
bpn_crm_id_admin: '%bpn_crm_id_admin%'
bpn_crm_id_team_admin: '%bpn_crm_id_team_admin%'
bpn_crm_id_manager: '%bpn_crm_id_manager%'
bpn_crm_id_teamer: '%bpn_crm_id_teamer%'
bpn_crm_house_manager_ids: '%bpn_crm_house_manager_ids%'
+44 -12
View File
@@ -30,17 +30,35 @@ assign:
| Role | Label | Granted by | Revoked by | Hierarchy |
|------|-------|-----------|------------|-----------|
| `ROLE_ADMIN` | Admin | super admin, approving a CRM claim | the CRM, automatically | ⇒ `ROLE_ADMINISTRATIVE` |
| `ROLE_TEAM_ADMIN` | Admin | super admin, approving a CRM claim | the CRM, automatically | ⇒ `ROLE_ADMINISTRATIVE` |
| `ROLE_MANAGER` | Reisemanager | super admin, approving a CRM claim | the CRM, automatically | ⇒ `ROLE_ADMINISTRATIVE` |
| `ROLE_HOUSE_MANAGER` | Hausleitung | super admin, approving a CRM claim | the CRM, automatically | — |
| `ROLE_TEAMER` | Teamer | the CRM, automatically | the CRM, automatically | — |
### A note on the `TEAM_` prefix
`ROLE_TEAM_ADMIN`, `ROLE_TEAM_ADMIN_PENDING` and `ROLE_TEAM_SUPER_ADMIN` were renamed from
`ROLE_ADMIN`, `ROLE_ADMIN_PENDING` and `ROLE_SUPER_ADMIN`. The MyE&P identity provider is
shared with a sibling portal that uses `ROLE_ADMIN` for a different privilege, so the plain
name was ambiguous across the estate; the prefix makes it unambiguously *this* application's
admin. The German label is unchanged — it still reads "Admin" everywhere in the UI.
Two consequences worth remembering:
- **`ROLE_ADMINISTRATIVE` is a different role and was not renamed.** It is granted only by
the hierarchy, never stored, and it shares the old `ROLE_ADMIN` prefix — so any
search-and-replace over role names must match on a word boundary
(`ROLE_TEAM_ADMIN(?![A-Z_])`) or it will corrupt ~100 call sites silently.
- **`ELIGIBLE_ROLES` in `MyEpAuthenticator` is a wire contract**, not an internal name: it is
compared directly against the IdP's `roles` claim. It only works while MyE&P emits
`ROLE_TEAM_ADMIN`, so the two sides have to move together.
`User::PENDING_ROLES` holds a marker for each of the three administrative roles, keyed by
the role it stands for:
| Marker | Meaning |
|--------|---------|
| `ROLE_ADMIN_PENDING` | the CRM claims this person is an admin, nobody has confirmed it |
| `ROLE_TEAM_ADMIN_PENDING` | the CRM claims this person is an admin, nobody has confirmed it |
| `ROLE_MANAGER_PENDING` | likewise for Reisemanager |
| `ROLE_HOUSE_MANAGER_PENDING` | likewise for Hausleitung |
@@ -50,10 +68,10 @@ effects are cosmetic (rendered as "Admin (nicht freigeschaltet)") and organisati
put the user on the approval list). `ROLE_TEAMER` has no marker: it needs no approval.
Two further roles are synthesized by `User::getRoles()` and never stored: `ROLE_USER` for
everybody, and `ROLE_SUPER_ADMIN` when the separate `superAdmin` boolean column is set. A
everybody, and `ROLE_TEAM_SUPER_ADMIN` when the separate `superAdmin` boolean column is set. A
validation callback (`User::validateSuperAdmin()`) refuses `superAdmin` without
`ROLE_ADMIN` alongside it — super admin is an elevation, never a standalone grant. The sync
enforces the same rule from the other side: revoking `ROLE_ADMIN` clears the flag, or the one
`ROLE_TEAM_ADMIN` alongside it — super admin is an elevation, never a standalone grant. The sync
enforces the same rule from the other side: revoking `ROLE_TEAM_ADMIN` clears the flag, or the one
role that outranks every check in the application would outlive the role it depends on.
### Storage and accessors
@@ -63,7 +81,7 @@ slice it, and picking the right one matters:
| Accessor | Returns |
|----------|---------|
| `getRoles()` | the column **plus** synthesized `ROLE_USER` / `ROLE_SUPER_ADMIN` — what Symfony authorises against |
| `getRoles()` | the column **plus** synthesized `ROLE_USER` / `ROLE_TEAM_SUPER_ADMIN` — what Symfony authorises against |
| `getAssignedRoles()` | only the four real roles from the column — what the sync works on |
| `getPendingRoles()` | only the markers |
| `getNominatedRoles()` | the roles behind those markers, as `role => label` — what an approver acts on |
@@ -77,7 +95,7 @@ slice it, and picking the right one matters:
| CRM attribute | Recognised by | Sets |
|---------------|---------------|------|
| admin | attribute id `%bpn_crm_id_admin%`, selected | `isAdmin` |
| admin | attribute id `%bpn_crm_id_team_admin%`, selected | `isAdmin` |
| Reisemanager | attribute id `%bpn_crm_id_manager%`, selected | `isManager` |
| teamer | attribute id `%bpn_crm_id_teamer%`, selected | `isTeamer` |
| Hausleitung | attribute id listed in `%bpn_crm_house_manager_ids%`, selected | `isHouseManager` + the hotel code that id maps to |
@@ -111,12 +129,26 @@ roles were revoked.
>
> | Parameter | Attribute |
> |-----------|-----------|
> | `APP_BPN_CRM_ID_ADMIN` | `Admin` |
> | `APP_BPN_CRM_ID_TEAM_ADMIN` | the team-admin selection — **not** the old portal-wide `Admin` (1292) |
> | `APP_BPN_CRM_ID_MANAGER` | `Manager` |
> | `APP_BPN_CRM_ID_TEAMER` | `E&P Teamer - allg. Merkmal` |
>
> Matching is by id and never by label, so `Preisrechner Admin` does not trip the admin flag.
>
> `APP_BPN_CRM_ID_TEAM_ADMIN` was renamed from `APP_BPN_CRM_ID_ADMIN` with the
> `ROLE_TEAM_ADMIN` rename, and its **value has to change too**. The old value 1292 is the
> portal-wide admin selection, which still means `ROLE_ADMIN` in the sibling portal — keeping
> it would have left this app granting its admin off the very selection the rename was meant
> to stop sharing. It ships as `0` — a valid int that matches no attribute, so the container
> boots but nobody is granted the role — and must be set to the new selection's id.
>
> **This is a hard cutover.** `revokeUnclaimedRoles()` withdraws any granted role the CRM no
> longer claims, and `revokeSuperAdminWithoutRoleAdmin()` takes the super admin flag down with
> `ROLE_TEAM_ADMIN`. So the new selection must exist **and already be assigned to every admin**
> in BusPro before this is deployed; otherwise each of them is demoted on their next login and
> needs a super admin to re-approve. Admins who also hold `ROLE_TEAMER` degrade to teamer
> access; an admin without it is blocked outright by `disableForRevokedCrmRoles()`.
>
> `bpn_crm_house_manager_ids` (`config/services.yaml`) is deployment-critical for the same
> reason, and more sharply so: since roles are synced, an id missing from that map does not
> merely fail to nominate a Hausleitung, it **revokes** the role from everyone holding it, one
@@ -137,7 +169,7 @@ roles were revoked.
`BpnAuthenticator::getOrCreateLocalUser()``UserDataHandler::createLocalUser()` writes
`collectRoles()` verbatim, together with the hotel codes from the Hausleitung attributes.
A CRM admin who is not also a teamer therefore starts with `['ROLE_ADMIN_PENDING']` and no
A CRM admin who is not also a teamer therefore starts with `['ROLE_TEAM_ADMIN_PENDING']` and no
privileges at all: they can authenticate, but `UserChecker` refuses the session until a
super admin approves them.
@@ -148,7 +180,7 @@ the roles to **`syncRoles()`**, which is the whole policy in four steps:
1. **revoke** every granted role the CRM no longer claims. This is what makes BusPro the
source of truth, and it applies to `ROLE_TEAMER` as much as to the administrative roles.
2. **clear the super admin flag** when `ROLE_ADMIN` was among them — `ROLE_SUPER_ADMIN` is
2. **clear the super admin flag** when `ROLE_TEAM_ADMIN` was among them — `ROLE_TEAM_SUPER_ADMIN` is
synthesized from a separate column and would otherwise survive its own precondition.
3. **`refreshPendingRoles()`** recomputes the marker set from the current claims. A marker
whose real role is already granted is dropped — an approved role is never marked again.
@@ -189,7 +221,7 @@ runs again on submit to catch a sync that revoked the claim while the dialog was
A denial is not recorded anywhere: as long as the CRM keeps claiming the role, the
nomination is back on the next login.
**Super admin** is only offered to somebody who already holds `ROLE_ADMIN` — approve first,
**Super admin** is only offered to somebody who already holds `ROLE_TEAM_ADMIN` — approve first,
elevate afterwards. The one exception is a flag that outlived its role, which stays editable
so the account can be saved at all while `User::validateSuperAdmin()` is violated; the sync
clears it (see 2), so it should never occur in practice.
@@ -243,7 +275,7 @@ administrative users, teamers are an admin's business:
| Surface | Who | Notes |
|---------|-----|-------|
| `/admin/teamer/disable-user/{uuid}` and `/administrative/teamer/enable-user/{uuid}` | `ROLE_ADMIN` | teamers; public reason mandatory, internal optional |
| `/admin/teamer/disable-user/{uuid}` and `/administrative/teamer/enable-user/{uuid}` | `ROLE_TEAM_ADMIN` | teamers; public reason mandatory, internal optional |
| "Account gesperrt" checkbox on the user edit form | super admin (`UserVoter`) | everyone else; both reasons optional |
Both go through `User::setDisabled()`, which is a no-op when the state is unchanged — saving
+3 -3
View File
@@ -200,7 +200,7 @@ class ResponseParser
$isHouseManager = true;
$hotelCodes[] = $houseManagerCode;
}
if ($this->config['bpn_crm_id_admin'] === $attribute->getId() && true === $attribute->isSelected()) {
if ($this->config['bpn_crm_id_team_admin'] === $attribute->getId() && true === $attribute->isSelected()) {
$isAdmin = true;
}
if ($this->config['bpn_crm_id_manager'] === $attribute->getId() && true === $attribute->isSelected()) {
@@ -305,11 +305,11 @@ class ResponseParser
private function resolveOptions(array $options): array
{
$optionsResolver = new OptionsResolver();
$optionsResolver->setRequired(['bpn_crm_id_admin', 'bpn_crm_id_manager', 'bpn_crm_id_teamer', 'bpn_crm_house_manager_ids']);
$optionsResolver->setRequired(['bpn_crm_id_team_admin', 'bpn_crm_id_manager', 'bpn_crm_id_teamer', 'bpn_crm_house_manager_ids']);
$optionsResolver->setDefaults([
'bpn_default_hotel_code' => null,
]);
$optionsResolver->setAllowedTypes('bpn_crm_id_admin', 'int');
$optionsResolver->setAllowedTypes('bpn_crm_id_team_admin', 'int');
$optionsResolver->setAllowedTypes('bpn_crm_id_manager', 'int');
$optionsResolver->setAllowedTypes('bpn_crm_id_teamer', 'int');
$optionsResolver->setAllowedTypes('bpn_crm_house_manager_ids', 'array');
+7 -7
View File
@@ -69,7 +69,7 @@ class UserDataHandler
$claimedRoles = [];
if ($crmAttributes->isAdmin()) {
$claimedRoles[] = 'ROLE_ADMIN';
$claimedRoles[] = 'ROLE_TEAM_ADMIN';
}
if ($crmAttributes->isManager()) {
@@ -370,7 +370,7 @@ class UserDataHandler
* The whole policy, in the order it has to run:
*
* 1. revoke what is no longer claimed - the identity source leads;
* 2. drop the super admin flag along with ROLE_ADMIN, or the highest privilege in the
* 2. drop the super admin flag along with ROLE_TEAM_ADMIN, or the highest privilege in the
* application would outlive the role it depends on;
* 3. refresh the pending markers, after the revocation so that a role just revoked is
* not immediately marked again - it is unclaimed in both steps;
@@ -424,7 +424,7 @@ class UserDataHandler
*
* Only the roles of User::ROLES are touched: getAssignedRoles() excludes the pending
* markers as well as the implicit ROLE_USER, and the markers are dealt with by
* refreshPendingRoles(). ROLE_SUPER_ADMIN is not a stored role at all but a flag, so
* refreshPendingRoles(). ROLE_TEAM_SUPER_ADMIN is not a stored role at all but a flag, so
* it is handled separately below.
*
* @param string[] $claimedRoles
@@ -450,22 +450,22 @@ class UserDataHandler
}
/**
* Takes the super admin flag down with ROLE_ADMIN.
* Takes the super admin flag down with ROLE_TEAM_ADMIN.
*
* The flag is stored on its own and getRoles() turns it into ROLE_SUPER_ADMIN whatever
* The flag is stored on its own and getRoles() turns it into ROLE_TEAM_SUPER_ADMIN whatever
* else the user holds, so without this a person the CRM no longer calls an admin would
* keep the one role that outranks every check in the application. User::validateSuperAdmin()
* enforces the same rule on the edit form, but only there.
*/
private function revokeSuperAdminWithoutRoleAdmin(User $user): void
{
if (false === $user->isSuperAdmin() || true === $user->hasRole('ROLE_ADMIN')) {
if (false === $user->isSuperAdmin() || true === $user->hasRole('ROLE_TEAM_ADMIN')) {
return;
}
$user->setSuperAdmin(false);
$this->logger->info('Revoke super admin flag along with ROLE_ADMIN', [
$this->logger->info('Revoke super admin flag along with ROLE_TEAM_ADMIN', [
'user_id' => $user->getId(),
'user_email' => $user->getEmail(),
]);
@@ -16,7 +16,7 @@ class UserController extends AbstractController
}
#[Route('/admin/autocomplete/user', name: 'app_admin_autocomplete_user')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): JsonResponse
{
try {
@@ -22,7 +22,7 @@ class ApproveController extends AbstractController
}
#[Route('/admin/feedback/approve/{uuid}', name: 'app_admin_feedback_approve')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Feedback $feedback, Request $request): Response
{
$form = $this->createForm(FeedbackApproveType::class, $feedback);
@@ -24,7 +24,7 @@ class ProvideController extends AbstractController
}
#[Route('/admin/feedback/provide', name: 'app_admin_feedback_provide')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$form = $this->getFeedbackForm();
@@ -58,7 +58,7 @@ class ProvideController extends AbstractController
}
#[Route('/admin/feedback/provide/form', name: 'app_admin_feedback_provide_form')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function form(Request $request): Response
{
$form = $this->getFeedbackForm();
+1 -1
View File
@@ -21,7 +21,7 @@ class IndexController extends AbstractController
}
#[Route('/admin', name: 'app_admin_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$applicationRepository = $this->entityManager->getRepository(Application::class);
+1 -1
View File
@@ -20,7 +20,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/log', name: 'app_admin_log_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$qb = $this
@@ -22,7 +22,7 @@ class CreateController extends AbstractController
}
#[Route('/admin/system/availability/create', name: 'app_admin_system_availability_create')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$availability = new Availability();
@@ -21,7 +21,7 @@ class DeleteController extends AbstractController
}
#[Route('/admin/system/availability/delete/{id}', name: 'app_admin_system_availability_delete', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Availability $availability, Request $request): Response
{
if (true === $request->isMethod('POST')) {
@@ -22,7 +22,7 @@ class DuplicateController extends AbstractController
}
#[Route('/admin/system/availability/duplicate/{id}', name: 'app_admin_system_availability_duplicate')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Availability $availability, Request $request): Response
{
$copy = Availability::duplicate($availability);
@@ -22,7 +22,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/availability/edit/{id}', name: 'app_admin_system_availability_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Availability $availability, Request $request): Response
{
$form = $this->createForm(AvailabilityType::class, $availability);
@@ -19,7 +19,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/availability', name: 'app_admin_system_availability_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$query = $this
@@ -28,7 +28,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/email-text/edit/{key}', name: 'app_admin_system_email_text_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(EmailTextKey $key, Request $request): Response
{
$definition = $this->catalog->get($key);
@@ -18,7 +18,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/email-text', name: 'app_admin_system_email_text_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
// The list is driven by the catalogue, not by the table: a mail that has never
@@ -25,7 +25,7 @@ class PreviewController extends AbstractController
* a real mail is being assembled.
*/
#[Route('/admin/system/email-text/preview/{key}', name: 'app_admin_system_email_text_preview')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(EmailTextKey $key): Response
{
$definition = $this->catalog->get($key);
@@ -27,7 +27,7 @@ class PreviewDraftController extends AbstractController
* the submitted values instead of the stored ones.
*/
#[Route('/admin/system/email-text/preview-draft/{key}', name: 'app_admin_system_email_text_preview_draft', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(EmailTextKey $key, Request $request): Response
{
$definition = $this->catalog->get($key);
@@ -25,7 +25,7 @@ class ResetController extends AbstractController
}
#[Route('/admin/system/email-text/reset/{key}', name: 'app_admin_system_email_text_reset')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(EmailTextKey $key, Request $request): Response
{
$emailText = $this->emailTextRepository->findByKey($key);
@@ -22,7 +22,7 @@ class CreateController extends AbstractController
}
#[Route('/admin/system/fee/create', name: 'app_admin_system_fee_create')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$fee = new Fee();
@@ -21,7 +21,7 @@ class DeleteController extends AbstractController
}
#[Route('/admin/system/fee/delete/{id}', name: 'app_admin_system_fee_delete')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Fee $fee, Request $request): Response
{
if (true === $request->isMethod('POST')) {
@@ -22,7 +22,7 @@ class DuplicateController extends AbstractController
}
#[Route('/admin/system/fee/duplicate/{id}', name: 'app_admin_system_fee_duplicate')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Fee $fee, Request $request): Response
{
$copy = Fee::duplicate($fee);
@@ -22,7 +22,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/fee/edit/{id}', name: 'app_admin_system_fee_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Fee $fee, Request $request): Response
{
$form = $this->createForm(FeeType::class, $fee);
@@ -15,7 +15,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/fee', name: 'app_admin_system_fee_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$fees = $this->feeRepository->getList();
@@ -21,7 +21,7 @@ class CreateController extends AbstractController
}
#[Route('/admin/system/feedback-set/create', name: 'app_admin_system_feedback_set_create')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$feedbackSet = new FeedbackSet();
@@ -21,7 +21,7 @@ class DeleteController extends AbstractController
}
#[Route('/admin/system/feedback-set/delete/{id}', name: 'app_admin_system_feedback_set_delete')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
#[IsGranted('DELETE', subject: 'feedbackSet')]
public function index(FeedbackSet $feedbackSet, Request $request): Response
{
@@ -21,7 +21,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/feedback-set/edit/{id}', name: 'app_admin_system_feedback_set_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(FeedbackSet $feedbackSet, Request $request): Response
{
$form = $this->createForm(FeedbackSetType::class, $feedbackSet);
@@ -15,7 +15,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/feedback-set', name: 'app_admin_system_feedback_set_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$feedbackSets = $this
@@ -21,7 +21,7 @@ class CreateController extends AbstractController
}
#[Route('/admin/system/job-profile/create', name: 'app_admin_system_job_profile_create')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$jobProfile = new JobProfile();
@@ -21,7 +21,7 @@ class DeleteController extends AbstractController
}
#[Route('/admin/system/job-profile/delete/{id}', name: 'app_admin_system_job_profile_delete')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(JobProfile $jobProfile, Request $request): Response
{
if (true === $request->isMethod('POST')) {
@@ -21,7 +21,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/job-profile/edit/{id}', name: 'app_admin_system_job_profile_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(JobProfile $jobProfile, Request $request): Response
{
$form = $this->createForm(JobProfileType::class, $jobProfile);
@@ -15,7 +15,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/job-profile', name: 'app_admin_system_job_profile_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$jobProfiles = $this
@@ -22,7 +22,7 @@ class CreateController extends AbstractController
}
#[Route('/admin/system/training/create', name: 'app_admin_system_training_create')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$training = new Training();
@@ -21,7 +21,7 @@ class DeleteController extends AbstractController
}
#[Route('/admin/system/training/delete/{id}', name: 'app_admin_system_training_delete')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Training $training, Request $request): Response
{
if (true === $request->isMethod('POST')) {
@@ -22,7 +22,7 @@ class EditController extends AbstractController
}
#[Route('/admin/system/training/edit/{id}', name: 'app_admin_system_training_edit')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Training $training, Request $request): Response
{
$form = $this->createForm(TrainingType::class, $training);
@@ -15,7 +15,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/training', name: 'app_admin_system_training_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$trainings = $this
@@ -15,7 +15,7 @@ class IndexController extends AbstractController
}
#[Route('/admin/system/user', name: 'app_admin_system_user_index')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(): Response
{
$users = $this->userRepository->getAdministrativeUsers();
@@ -11,7 +11,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
class CrmSelectionsController extends AbstractController
{
#[Route('/admin/teamer/crm-selections/{uuid}', name: 'app_admin_teamer_crm_selections')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Teamer $teamer): Response
{
return $this->render('admin/teamer/crm_selections.html.twig', [
@@ -23,7 +23,7 @@ class DeleteAccountController extends AbstractController
}
#[Route('/admin/teamer/delete-account/{uuid}', name: 'app_admin_teamer_delete_account')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Teamer $teamer, Request $request): Response
{
if (true === $request->isMethod(Request::METHOD_POST)) {
@@ -44,7 +44,7 @@ class DeleteAccountController extends AbstractController
}
#[Route('/admin/teamer/restore-account/{uuid}', name: 'app_admin_teamer_restore_account')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function restore(Teamer $teamer, Request $request): Response
{
if (true === $request->isMethod(Request::METHOD_POST)) {
@@ -22,7 +22,7 @@ class DisableUserController extends AbstractController
}
#[Route('/admin/teamer/disable-user/{uuid}', name: 'app_admin_teamer_disable_user')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Teamer $teamer, Request $request): Response
{
$user = $teamer->getUser();
@@ -50,7 +50,7 @@ class DisableUserController extends AbstractController
}
#[Route('/administrative/teamer/enable-user/{uuid}', name: 'app_admin_teamer_enable_user')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function enable(Teamer $teamer, Request $request): Response
{
$user = $teamer->getUser();
@@ -39,7 +39,7 @@ class MailingController extends AbstractController
* the real send goes through the confirmation modal below.
*/
#[Route('/admin/teamer/mailing', name: 'app_admin_teamer_mailing')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Request $request): Response
{
$form = $this->createMailingForm($request);
@@ -82,7 +82,7 @@ class MailingController extends AbstractController
* one that was parked.
*/
#[Route('/admin/teamer/mailing/draft', name: 'app_admin_teamer_mailing_draft', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function draft(Request $request): Response
{
$mailingDto = $this->createMailingForm($request)->getData();
@@ -93,7 +93,7 @@ class MailingController extends AbstractController
}
#[Route('/admin/teamer/mailing/discard', name: 'app_admin_teamer_mailing_discard')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function discard(): Response
{
$this->draftHandler->resetDraft();
@@ -102,7 +102,7 @@ class MailingController extends AbstractController
}
#[Route('/admin/teamer/mailing/confirm', name: 'app_admin_teamer_mailing_confirm', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function confirm(Request $request): Response
{
$form = $this->createMailingForm($request);
@@ -119,7 +119,7 @@ class MailingController extends AbstractController
}
#[Route('/admin/teamer/mailing/send', name: 'app_admin_teamer_mailing_send', methods: ['POST'])]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function send(Request $request): Response
{
$form = $this->createMailingForm($request);
@@ -21,7 +21,7 @@ class RemarksController extends AbstractController
}
#[Route('/admin/teamer/remarks/{uuid}', name: 'app_admin_teamer_remarks_internal')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Teamer $teamer, Request $request): Response
{
$returnUrl = $this->getReturnUrl($request, 'app_administrative_teamer_index');
@@ -25,7 +25,7 @@ class SkillsController extends AbstractController
}
#[Route('/admin/teamer/skills/{uuid}', name: 'app_admin_teamer_skills')]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(Teamer $teamer, Request $request): Response
{
$trainings = $this->trainingRepository->getList();
@@ -34,7 +34,7 @@ class CreateController extends AbstractController
path: '/admin/teamer/skills/training-attendance/create/{training_id}/{teamer_id}',
name: 'app_admin_teamer_skills_training_attendance_create'
)]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(
#[MapEntity(mapping: ['training_id' => 'id'])]
Training $training,
@@ -30,7 +30,7 @@ class DeleteController extends AbstractController
path: '/admin/teamer/skills/training-attendance/delete/{uuid}',
name: 'app_admin_teamer_skills_training_attendance_delete'
)]
#[IsGranted('ROLE_ADMIN')]
#[IsGranted('ROLE_TEAM_ADMIN')]
public function index(TrainingAttendance $attendance, Request $request): Response
{
if (true === $request->isMethod('POST')) {
+7 -7
View File
@@ -22,7 +22,7 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
* Assignable roles and their labels.
*/
public const ROLES = [
'ROLE_ADMIN' => 'Admin',
'ROLE_TEAM_ADMIN' => 'Team Admin',
'ROLE_MANAGER' => 'Reisemanager',
'ROLE_HOUSE_MANAGER' => 'Hausleitung',
'ROLE_TEAMER' => 'Teamer',
@@ -35,7 +35,7 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
* a super admin.
*/
public const PENDING_ROLES = [
'ROLE_ADMIN' => 'ROLE_ADMIN_PENDING',
'ROLE_TEAM_ADMIN' => 'ROLE_TEAM_ADMIN_PENDING',
'ROLE_MANAGER' => 'ROLE_MANAGER_PENDING',
'ROLE_HOUSE_MANAGER' => 'ROLE_HOUSE_MANAGER_PENDING',
];
@@ -194,7 +194,7 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
$roles = ['ROLE_USER', ...$this->roles];
if (true === $this->isSuperAdmin()) {
$roles[] = 'ROLE_SUPER_ADMIN';
$roles[] = 'ROLE_TEAM_SUPER_ADMIN';
}
return array_unique($roles);
@@ -223,7 +223,7 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
/**
* The manually assignable roles held by the user, i.e. without the implicit ROLE_USER
* and ROLE_SUPER_ADMIN added by getRoles() and without any pending marker. Used to
* and ROLE_TEAM_SUPER_ADMIN added by getRoles() and without any pending marker. Used to
* edit role assignments: saving them resolves the pending approvals.
*/
public function getAssignedRoles(): array
@@ -283,12 +283,12 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
}
/**
* Super admin is an elevation of ROLE_ADMIN, never a standalone grant.
* Super admin is an elevation of ROLE_TEAM_ADMIN, never a standalone grant.
*/
#[Assert\Callback]
public function validateSuperAdmin(ExecutionContextInterface $context): void
{
if (true === $this->superAdmin && false === in_array('ROLE_ADMIN', $this->roles, true)) {
if (true === $this->superAdmin && false === in_array('ROLE_TEAM_ADMIN', $this->roles, true)) {
$context
->buildViolation('Nur Admins können zu Superadmins ernannt werden.')
->atPath('superAdmin')
@@ -311,7 +311,7 @@ class User implements UserInterface, TimestampableEntityInterface, SoftDeletable
public function getDefaultRoute(): string
{
if ($this->hasRole('ROLE_ADMIN')) {
if ($this->hasRole('ROLE_TEAM_ADMIN')) {
return 'app_admin_index';
} elseif ($this->hasRole('ROLE_MANAGER')) {
return 'app_manager_index';
+2 -2
View File
@@ -67,7 +67,7 @@ class UserType extends AbstractType
});
// Super admin is an elevation of an existing role, never a grant of its own, so the
// field exists only for somebody who already holds ROLE_ADMIN - offering it to
// field exists only for somebody who already holds ROLE_TEAM_ADMIN - offering it to
// anyone else would only produce the violation from User::validateSuperAdmin().
// A flag already set without the role is the exception: it has to stay editable, or
// that user could not be saved at all until BusPro claims them an admin again.
@@ -78,7 +78,7 @@ class UserType extends AbstractType
return;
}
if (false === in_array('ROLE_ADMIN', $user->getAssignedRoles(), true) && false === $user->isSuperAdmin()) {
if (false === in_array('ROLE_TEAM_ADMIN', $user->getAssignedRoles(), true) && false === $user->isSuperAdmin()) {
return;
}
+3 -3
View File
@@ -19,7 +19,7 @@ abstract class AbstractMenuBuilder
* Note that "app_admin_" does not match the shared "app_administrative_" routes.
*/
protected const AREA_ROUTE_PREFIXES = [
'app_admin_' => 'ROLE_ADMIN',
'app_admin_' => 'ROLE_TEAM_ADMIN',
'app_manager_' => 'ROLE_MANAGER',
'app_house_manager_' => 'ROLE_HOUSE_MANAGER',
'app_teamer_' => 'ROLE_TEAMER',
@@ -29,7 +29,7 @@ abstract class AbstractMenuBuilder
* Priority order of the area roles, must stay in sync with User::getDefaultRoute().
*/
protected const ROLE_PRIORITY = [
'ROLE_ADMIN',
'ROLE_TEAM_ADMIN',
'ROLE_MANAGER',
'ROLE_HOUSE_MANAGER',
'ROLE_TEAMER',
@@ -140,7 +140,7 @@ abstract class AbstractMenuBuilder
protected function addAdminItem(ItemInterface $menu): void
{
if ($this->security->isGranted('ROLE_ADMIN')) {
if ($this->security->isGranted('ROLE_TEAM_ADMIN')) {
$this->addDivider($menu);
$menu->addChild('zum Adminbereich', [
'route' => 'app_admin_index',
+2 -2
View File
@@ -32,7 +32,7 @@ class MenuBuilder extends AbstractMenuBuilder
public function createMainMenu(array $options): ItemInterface
{
return match ($this->resolveArea()) {
'ROLE_ADMIN' => $this->adminMenuBuilder->createMainMenu($options),
'ROLE_TEAM_ADMIN' => $this->adminMenuBuilder->createMainMenu($options),
'ROLE_MANAGER' => $this->managerMenuBuilder->createMainMenu($options),
'ROLE_HOUSE_MANAGER' => $this->houseManagerMenuBuilder->createMainMenu($options),
'ROLE_TEAMER' => $this->teamerMenuBuilder->createMainMenu($options),
@@ -43,7 +43,7 @@ class MenuBuilder extends AbstractMenuBuilder
public function createTeamerMenu(array $options): ItemInterface
{
return match ($this->resolveArea()) {
'ROLE_ADMIN' => $this->adminMenuBuilder->createTeamerMenu($options),
'ROLE_TEAM_ADMIN' => $this->adminMenuBuilder->createTeamerMenu($options),
'ROLE_MANAGER' => $this->managerMenuBuilder->createTeamerMenu($options),
default => $this->createRootElement(),
};
@@ -7,7 +7,7 @@ use App\Entity\User;
abstract class AbstractRequiredTeamerCheck implements RequiredTeamerCheckInterface
{
private const EXCLUDED_ROLES = [
'ROLE_ADMIN',
'ROLE_TEAM_ADMIN',
'ROLE_MANAGER',
'ROLE_HOUSE_MANAGER',
];
+1 -1
View File
@@ -35,7 +35,7 @@ class MyEpAuthenticator extends AbstractAuthenticator
* is dropped rather than stored, so that no role this application assigns a meaning
* to can be set from the outside.
*/
private const ELIGIBLE_ROLES = ['ROLE_ADMIN', 'ROLE_TEAMER', 'ROLE_MANAGER', 'ROLE_HOUSE_MANAGER'];
private const ELIGIBLE_ROLES = ['ROLE_TEAM_ADMIN', 'ROLE_TEAMER', 'ROLE_MANAGER', 'ROLE_HOUSE_MANAGER'];
public function __construct(
private readonly MyEpClient $client,
+2 -2
View File
@@ -60,7 +60,7 @@ class DispositionVoter extends Voter
static::CONTRACT, static::INVOICE => false === $disposition->isSkipFormalities()
&& ($this->security->isGranted('ROLE_ADMINISTRATIVE')
|| $this->assertTeamerAccess($token, $disposition)),
static::DELETE => $this->security->isGranted('ROLE_ADMIN'),
static::DELETE => $this->security->isGranted('ROLE_TEAM_ADMIN'),
static::FEEDBACK => false === $disposition->isSkipFormalities()
&& ($this->security->isGranted('ROLE_ADMINISTRATIVE')
|| $this->assertHouseManagerAccess($token, $disposition)),
@@ -162,7 +162,7 @@ class DispositionVoter extends Voter
private function assertAdminDocumentUploadAllowed(Disposition $disposition): bool
{
if (false === $this->security->isGranted('ROLE_ADMINISTRATIVE')
&& false === $this->security->isGranted('ROLE_ADMIN')) {
&& false === $this->security->isGranted('ROLE_TEAM_ADMIN')) {
return false;
}
+1 -1
View File
@@ -24,7 +24,7 @@ class FeedbackVoter extends Voter
/** @var Feedback $feedback */
$feedback = $subject;
if (in_array('ROLE_ADMIN', $token->getRoleNames())) {
if (in_array('ROLE_TEAM_ADMIN', $token->getRoleNames())) {
return true;
}
+1 -1
View File
@@ -52,7 +52,7 @@ class ImpersonationVoter extends Voter
}
// Admin is the only role allowed to impersonate
if (false === $this->security->isGranted('ROLE_ADMIN')) {
if (false === $this->security->isGranted('ROLE_TEAM_ADMIN')) {
return false;
}
+1 -1
View File
@@ -33,7 +33,7 @@ class AppRuntime implements RuntimeExtensionInterface
$user = $teamer->getUser();
if (null !== $user) {
if ($user->hasRole('ROLE_ADMIN')) {
if ($user->hasRole('ROLE_TEAM_ADMIN')) {
$labelItems[] = 'Admin';
}
+1 -1
View File
@@ -1,6 +1,6 @@
{{ form_start(form) }}
<div class="flex flex-col space-y-4 pb-8">
{# only present for somebody who already holds ROLE_ADMIN, see UserType #}
{# only present for somebody who already holds ROLE_TEAM_ADMIN, see UserType #}
{% if form.superAdmin is defined %}
{{ form_row(form.superAdmin) }}
{% endif %}
@@ -9,7 +9,7 @@
<div class="pb-8">
{% include '_partials/_assignment_info_compact.html.twig' %}
</div>
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<div class="col-span-2">
<h2 class="font-bold text-lg pb-4">
Bewerbungen
@@ -52,7 +52,7 @@
<th>
{{ knp_pagination_sortable(pagination, 'Busbegleitung', 'assignment.pickup') }}
</th>
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<th>
Bewerbungen
</th>
@@ -131,7 +131,7 @@
-
{% endif %}
</td>
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<td>
<a href="{{ path('app_administrative_assignment_detail', { 'uuid': assignment.uuid, 'r': return_url() }) }}" class="flex items-center space-x-2">
<span>{{ assignment.applications|length }}</span>
@@ -102,7 +102,7 @@
{{ knp_pagination_render(pagination) }}
</div>
</div>
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<a href="{{ path('app_admin_feedback_provide') }}" class="btn" title="Feedback erfassen">
Neu
</a>
@@ -26,7 +26,7 @@
Reset
</a>
{% endif %}
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<a href="{{ path('app_admin_teamer_mailing', { 'r': return_url() }) }}"
class="btn btn--small"
title="Mail an die gefilterte Liste schreiben">
@@ -113,7 +113,7 @@
</button>
{% endif %}
{% if teamer.deleted %}
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<button type="button"
role="menuitem"
tabindex="-1"
@@ -125,7 +125,7 @@
</button>
{% endif %}
{% elseif teamer.user is not null and teamer.user.disabled %}
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<button type="button"
role="menuitem"
tabindex="-1"
@@ -136,7 +136,7 @@
</button>
{% endif %}
{% else %}
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<button type="button"
class="text-red-500"
role="menuitem"
@@ -152,7 +152,7 @@
{{ icon('mask') }}
</a>
{% endif %}
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<button type="button"
class="text-red-500"
role="menuitem"
@@ -80,7 +80,7 @@
</li>
{% endfor %}
</ul>
{% if is_granted('ROLE_ADMIN') %}
{% if is_granted('ROLE_TEAM_ADMIN') %}
<h2 class="text-lg font-bold">
Interne Anmerkungen
</h2>
+1 -1
View File
@@ -186,7 +186,7 @@ class ResponseParserTest extends TestCase
private function getParserInstance(): ResponseParser
{
return new ResponseParser([
'bpn_crm_id_admin' => 1292,
'bpn_crm_id_team_admin' => 1292,
'bpn_crm_id_manager' => 1293,
'bpn_crm_id_teamer' => 1070,
// Deliberately a small excerpt of the configured map: the fixtures only carry
+35 -35
View File
@@ -44,7 +44,7 @@ class UserDataHandlerTest extends TestCase
{
yield 'admin only yields the pending marker' => [
(new CrmAttributesResponse())->setAdmin(true),
[User::PENDING_ROLES['ROLE_ADMIN']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN']],
];
yield 'manager only yields the pending marker' => [
@@ -64,12 +64,12 @@ class UserDataHandlerTest extends TestCase
yield 'admin and teamer' => [
(new CrmAttributesResponse())->setAdmin(true)->setTeamer(true),
[User::PENDING_ROLES['ROLE_ADMIN'], 'ROLE_TEAMER'],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN'], 'ROLE_TEAMER'],
];
yield 'admin and manager yield both markers' => [
(new CrmAttributesResponse())->setAdmin(true)->setManager(true),
[User::PENDING_ROLES['ROLE_ADMIN'], User::PENDING_ROLES['ROLE_MANAGER']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN'], User::PENDING_ROLES['ROLE_MANAGER']],
];
yield 'manager and house manager yield both markers, the roles stand on their own' => [
@@ -100,9 +100,9 @@ class UserDataHandlerTest extends TestCase
public static function toPendingRolesProvider(): iterable
{
yield 'admin' => [
['ROLE_ADMIN'],
['ROLE_TEAM_ADMIN'],
(new CrmAttributesResponse())->setAdmin(true),
[User::PENDING_ROLES['ROLE_ADMIN']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN']],
];
yield 'manager and house manager are marked independently' => [
@@ -118,9 +118,9 @@ class UserDataHandlerTest extends TestCase
];
yield 'admin and house manager' => [
['ROLE_ADMIN', 'ROLE_HOUSE_MANAGER'],
['ROLE_TEAM_ADMIN', 'ROLE_HOUSE_MANAGER'],
(new CrmAttributesResponse())->setAdmin(true)->setHouseManager(true),
[User::PENDING_ROLES['ROLE_ADMIN'], User::PENDING_ROLES['ROLE_HOUSE_MANAGER']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN'], User::PENDING_ROLES['ROLE_HOUSE_MANAGER']],
];
yield 'teamer has no marker' => [
@@ -144,7 +144,7 @@ class UserDataHandlerTest extends TestCase
->setEmail('[email protected]')
->setBusProAddressId(1)
->setBusProPersonId(2)
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setHotelCodes(['XYZ'])
;
@@ -172,7 +172,7 @@ class UserDataHandlerTest extends TestCase
$profileResponse,
true,
['team' => ['selected' => true]],
['ROLE_ADMIN', 'ROLE_TEAMER'],
['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'],
['DKS'],
);
@@ -182,7 +182,7 @@ class UserDataHandlerTest extends TestCase
// the CRM leads: the still claimed role survives, the houses are replaced by its own
$this->assertSame(['DKS'], $user->getHotelCodes());
$this->assertTrue($user->hasRole('ROLE_ADMIN'));
$this->assertTrue($user->hasRole('ROLE_TEAM_ADMIN'));
$this->assertSame('New', $teamer->getFirstName());
$this->assertSame('Lastname', $teamer->getLastName());
@@ -248,9 +248,9 @@ class UserDataHandlerTest extends TestCase
yield 'a claim beyond the approved role stays pending' => [
['ROLE_MANAGER'],
['ROLE_ADMIN', 'ROLE_MANAGER'],
['ROLE_TEAM_ADMIN', 'ROLE_MANAGER'],
['ROLE_MANAGER'],
[User::PENDING_ROLES['ROLE_ADMIN']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN']],
];
yield 'the claimed role changes' => [
@@ -261,7 +261,7 @@ class UserDataHandlerTest extends TestCase
];
yield 'a granted role is revoked once the CRM stops claiming it' => [
['ROLE_ADMIN', 'ROLE_TEAMER'],
['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'],
['ROLE_TEAMER'],
['ROLE_TEAMER'],
[],
@@ -282,14 +282,14 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles([User::PENDING_ROLES['ROLE_ADMIN']])
->setRoles([User::PENDING_ROLES['ROLE_TEAM_ADMIN']])
;
$handler = new UserDataHandler($this->entityManager, $this->logger);
$handler->updateLocalUser($user, $this->createProfileResponse(), true, [], ['ROLE_ADMIN', 'ROLE_TEAMER']);
$handler->updateLocalUser($user, $this->createProfileResponse(), true, [], ['ROLE_TEAM_ADMIN', 'ROLE_TEAMER']);
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame([User::PENDING_ROLES['ROLE_ADMIN']], $user->getPendingRoles());
$this->assertSame([User::PENDING_ROLES['ROLE_TEAM_ADMIN']], $user->getPendingRoles());
}
/**
@@ -302,13 +302,13 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN', 'ROLE_TEAMER'])
->setRoles(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'])
;
$handler = new UserDataHandler($this->entityManager, $this->logger);
$handler->updateLocalUser($user, $this->createProfileResponse(), false, [], ['ROLE_ADMIN']);
$handler->updateLocalUser($user, $this->createProfileResponse(), false, [], ['ROLE_TEAM_ADMIN']);
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
}
public function testUpdateLocalUserGrantsTheTeamerRoleOnlyOnce(): void
@@ -327,8 +327,8 @@ class UserDataHandlerTest extends TestCase
}
/**
* ROLE_SUPER_ADMIN is not a stored role but a flag getRoles() turns into one, so
* revoking ROLE_ADMIN has to take it down explicitly - otherwise the highest privilege
* ROLE_TEAM_SUPER_ADMIN is not a stored role but a flag getRoles() turns into one, so
* revoking ROLE_TEAM_ADMIN has to take it down explicitly - otherwise the highest privilege
* in the application would outlive the role it depends on.
*/
public function testUpdateLocalUserTakesTheSuperAdminFlagDownWithRoleAdmin(): void
@@ -337,7 +337,7 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN', 'ROLE_TEAMER'])
->setRoles(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'])
->setSuperAdmin(true)
;
@@ -346,7 +346,7 @@ class UserDataHandlerTest extends TestCase
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertFalse($user->isSuperAdmin());
$this->assertFalse($user->hasRole('ROLE_SUPER_ADMIN'));
$this->assertFalse($user->hasRole('ROLE_TEAM_SUPER_ADMIN'));
}
public function testUpdateLocalUserKeepsTheSuperAdminFlagOfAStillClaimedAdmin(): void
@@ -355,15 +355,15 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN', 'ROLE_TEAMER'])
->setRoles(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'])
->setSuperAdmin(true)
;
$handler = new UserDataHandler($this->entityManager, $this->logger);
$handler->updateLocalUser($user, $this->createProfileResponse(), false, [], ['ROLE_ADMIN']);
$handler->updateLocalUser($user, $this->createProfileResponse(), false, [], ['ROLE_TEAM_ADMIN']);
$this->assertTrue($user->isSuperAdmin());
$this->assertTrue($user->hasRole('ROLE_SUPER_ADMIN'));
$this->assertTrue($user->hasRole('ROLE_TEAM_SUPER_ADMIN'));
}
public function testUpdateLocalUserReplacesTheHotelCodesWithTheOnesTheCrmReports(): void
@@ -394,7 +394,7 @@ class UserDataHandlerTest extends TestCase
->setEmail('[email protected]')
->setRoles([
'ROLE_TEAMER',
User::PENDING_ROLES['ROLE_ADMIN'],
User::PENDING_ROLES['ROLE_TEAM_ADMIN'],
User::PENDING_ROLES['ROLE_HOUSE_MANAGER'],
])
;
@@ -403,8 +403,8 @@ class UserDataHandlerTest extends TestCase
$handler = new UserDataHandler($this->entityManager, $this->logger);
$this->assertTrue($handler->approveRole($user, 'ROLE_ADMIN'));
$this->assertSame(['ROLE_TEAMER', 'ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertTrue($handler->approveRole($user, 'ROLE_TEAM_ADMIN'));
$this->assertSame(['ROLE_TEAMER', 'ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
// the other nomination is untouched: one decision at a time
$this->assertSame([User::PENDING_ROLES['ROLE_HOUSE_MANAGER']], $user->getPendingRoles());
@@ -437,13 +437,13 @@ class UserDataHandlerTest extends TestCase
{
yield 'the CRM never claimed it' => [['ROLE_TEAMER'], 'ROLE_MANAGER'];
yield 'a different role is nominated' => [[User::PENDING_ROLES['ROLE_MANAGER']], 'ROLE_ADMIN'];
yield 'a different role is nominated' => [[User::PENDING_ROLES['ROLE_MANAGER']], 'ROLE_TEAM_ADMIN'];
yield 'already granted, so there is no marker left' => [['ROLE_ADMIN'], 'ROLE_ADMIN'];
yield 'already granted, so there is no marker left' => [['ROLE_TEAM_ADMIN'], 'ROLE_TEAM_ADMIN'];
yield 'teamer has no nomination to approve' => [['ROLE_TEAMER'], 'ROLE_TEAMER'];
yield 'not a role at all' => [[User::PENDING_ROLES['ROLE_ADMIN']], 'ROLE_SUPER_ADMIN'];
yield 'not a role at all' => [[User::PENDING_ROLES['ROLE_TEAM_ADMIN']], 'ROLE_TEAM_SUPER_ADMIN'];
}
public function testDisableForRevokedCrmRolesBlocksTheUserAndDropsThePendingMarkers(): void
@@ -452,7 +452,7 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN', User::PENDING_ROLES['ROLE_MANAGER']])
->setRoles(['ROLE_TEAM_ADMIN', User::PENDING_ROLES['ROLE_MANAGER']])
;
$this->entityManager
@@ -468,7 +468,7 @@ class UserDataHandlerTest extends TestCase
$this->assertSame('Automatisch gesperrt: keine Rollen in BusPro.', $user->getDisabledReasonInternal());
// the granted role is kept so the user stays reviewable, the marker is not
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
$this->assertSame([], $user->getPendingRoles());
}
@@ -480,7 +480,7 @@ class UserDataHandlerTest extends TestCase
->setFirstName('First')
->setLastName('Last')
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setDisabledAt($disabledAt)
->setDisabledReason('Wegen Fehlverhaltens gesperrt.')
->setDisabledReasonInternal('Siehe Vorgang 4711.')
+6 -6
View File
@@ -30,7 +30,7 @@ class UserTest extends TestCase
public function testPendingMarkersAreLabelledButNotAssignable(): void
{
$user = (new User())->setRoles([
User::PENDING_ROLES['ROLE_ADMIN'],
User::PENDING_ROLES['ROLE_TEAM_ADMIN'],
User::PENDING_ROLES['ROLE_HOUSE_MANAGER'],
'ROLE_TEAMER',
]);
@@ -41,7 +41,7 @@ class UserTest extends TestCase
);
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame(
[User::PENDING_ROLES['ROLE_ADMIN'], User::PENDING_ROLES['ROLE_HOUSE_MANAGER']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN'], User::PENDING_ROLES['ROLE_HOUSE_MANAGER']],
$user->getPendingRoles(),
);
}
@@ -62,7 +62,7 @@ class UserTest extends TestCase
public function testSuperAdminWithRoleAdminIsValid(): void
{
$user = (new User())
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setSuperAdmin(true)
;
@@ -170,19 +170,19 @@ class UserTest extends TestCase
{
$user = (new User())->setRoles([
'ROLE_TEAMER',
User::PENDING_ROLES['ROLE_ADMIN'],
User::PENDING_ROLES['ROLE_TEAM_ADMIN'],
User::PENDING_ROLES['ROLE_HOUSE_MANAGER'],
]);
$this->assertSame(
['ROLE_ADMIN' => 'Admin', 'ROLE_HOUSE_MANAGER' => 'Hausleitung'],
['ROLE_TEAM_ADMIN' => 'Admin', 'ROLE_HOUSE_MANAGER' => 'Hausleitung'],
$user->getNominatedRoles(),
);
}
public function testAGrantedRoleIsNotNominated(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$user = (new User())->setRoles(['ROLE_TEAM_ADMIN']);
$this->assertSame([], $user->getNominatedRoles());
}
+10 -10
View File
@@ -19,7 +19,7 @@ class UserTypeTest extends KernelTestCase
public function testRolesAndHotelCodesAreNotFields(): void
{
$user = (new User())
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setHotelCodes(['DKS'])
;
@@ -39,7 +39,7 @@ class UserTypeTest extends KernelTestCase
$form = $this->createForm($user);
$form->submit([
'roles' => ['ROLE_ADMIN'],
'roles' => ['ROLE_TEAM_ADMIN'],
'hotelCodes' => ['DKS'],
'disabled' => null,
]);
@@ -49,14 +49,14 @@ class UserTypeTest extends KernelTestCase
}
/**
* Super admin is an elevation of ROLE_ADMIN, so it is not on offer for anybody else -
* Super admin is an elevation of ROLE_TEAM_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->assertTrue($this->createForm((new User())->setRoles(['ROLE_TEAM_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'));
$this->assertFalse($this->createForm((new User())->setRoles([User::PENDING_ROLES['ROLE_TEAM_ADMIN']]))->has('superAdmin'));
}
/**
@@ -84,7 +84,7 @@ class UserTypeTest extends KernelTestCase
public function testSuperAdminIsAppointed(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$user = (new User())->setRoles(['ROLE_TEAM_ADMIN']);
$form = $this->createForm($user);
$form->submit([
@@ -98,7 +98,7 @@ class UserTypeTest extends KernelTestCase
public function testSubmitBlocksTheAccountWithAReason(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$user = (new User())->setRoles(['ROLE_TEAM_ADMIN']);
$form = $this->createForm($user);
$form->submit([
@@ -117,7 +117,7 @@ class UserTypeTest extends KernelTestCase
public function testSubmitUnblocksTheAccountAndClearsTheReasons(): void
{
$user = (new User())
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setDisabledAt(new \DateTimeImmutable('2026-01-01 08:00:00'))
->setDisabledReason('Für deinen Account liegt in BusPro keine Berechtigung mehr vor.')
->setDisabledReasonInternal('Automatisch gesperrt: keine Rollen in BusPro.')
@@ -135,13 +135,13 @@ class UserTypeTest extends KernelTestCase
$this->assertFalse($user->isDisabled());
$this->assertNull($user->getDisabledReason());
$this->assertNull($user->getDisabledReasonInternal());
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
}
public function testSubmitUnblockingClearsTheReasonsEvenWhenTheirFieldsAreStillFilled(): void
{
$user = (new User())
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setDisabledAt(new \DateTimeImmutable('2026-01-01 08:00:00'))
->setDisabledReason('Für deinen Account liegt in BusPro keine Berechtigung mehr vor.')
->setDisabledReasonInternal('Automatisch gesperrt: keine Rollen in BusPro.')
+3 -3
View File
@@ -51,7 +51,7 @@ class BpnAuthenticatorTest extends TestCase
public function testExistingUserWithoutClaimedRolesIsBlockedAndReturned(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$user = (new User())->setRoles(['ROLE_TEAM_ADMIN']);
$this->stubApiClient($this->createCrmAttributes());
@@ -76,7 +76,7 @@ class BpnAuthenticatorTest extends TestCase
*/
public function testResponseWithoutAttributeGroupsRefusesTheLoginWithoutBlocking(): void
{
$user = (new User())->setRoles(['ROLE_ADMIN']);
$user = (new User())->setRoles(['ROLE_TEAM_ADMIN']);
// an empty payload carries no roles either and must not read as a revocation
$this->stubApiClient(new CrmAttributesResponse());
@@ -93,7 +93,7 @@ class BpnAuthenticatorTest extends TestCase
} catch (UserNotFoundException) {
}
$this->assertSame(['ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
}
/**
+12 -12
View File
@@ -60,11 +60,11 @@ class MyEpAuthenticatorTest extends TestCase
$this->repository->method('findOneBy')->willReturn(null);
$this->repository->method('findBy')->willReturn([]);
$user = $this->loadUser($this->createUserinfo(['ROLE_ADMIN', 'ROLE_TEAMER']));
$user = $this->loadUser($this->createUserinfo(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER']));
$this->assertSame([User::PENDING_ROLES['ROLE_ADMIN']], $user->getPendingRoles());
$this->assertSame([User::PENDING_ROLES['ROLE_TEAM_ADMIN']], $user->getPendingRoles());
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertNotContains('ROLE_ADMIN', $user->getRoles());
$this->assertNotContains('ROLE_TEAM_ADMIN', $user->getRoles());
$this->assertContains('ROLE_TEAMER', $user->getRoles());
}
@@ -118,14 +118,14 @@ class MyEpAuthenticatorTest extends TestCase
$user = (new User())->setEmail('[email protected]')->setRoles(['ROLE_TEAMER']);
$this->repository->method('findOneBy')->willReturn($user);
$this->loadUser($this->createUserinfo(['ROLE_ADMIN', 'ROLE_MANAGER', 'ROLE_TEAMER']));
$this->loadUser($this->createUserinfo(['ROLE_TEAM_ADMIN', 'ROLE_MANAGER', 'ROLE_TEAMER']));
$this->assertSame(['ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame(
[User::PENDING_ROLES['ROLE_ADMIN'], User::PENDING_ROLES['ROLE_MANAGER']],
[User::PENDING_ROLES['ROLE_TEAM_ADMIN'], User::PENDING_ROLES['ROLE_MANAGER']],
$user->getPendingRoles(),
);
$this->assertNotContains('ROLE_ADMIN', $user->getRoles());
$this->assertNotContains('ROLE_TEAM_ADMIN', $user->getRoles());
}
/**
@@ -133,12 +133,12 @@ class MyEpAuthenticatorTest extends TestCase
*/
public function testAnAlreadyGrantedAdministrativeRoleIsKeptWhileStillClaimed(): void
{
$user = (new User())->setEmail('[email protected]')->setRoles(['ROLE_ADMIN', 'ROLE_TEAMER']);
$user = (new User())->setEmail('[email protected]')->setRoles(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER']);
$this->repository->method('findOneBy')->willReturn($user);
$this->loadUser($this->createUserinfo(['ROLE_ADMIN', 'ROLE_TEAMER']));
$this->loadUser($this->createUserinfo(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER']));
$this->assertSame(['ROLE_ADMIN', 'ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'], $user->getAssignedRoles());
$this->assertSame([], $user->getPendingRoles());
}
@@ -150,7 +150,7 @@ class MyEpAuthenticatorTest extends TestCase
{
$user = (new User())
->setEmail('[email protected]')
->setRoles(['ROLE_ADMIN', 'ROLE_TEAMER'])
->setRoles(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER'])
->setSuperAdmin(true)
->setHotelCodes(['SSL'])
;
@@ -163,7 +163,7 @@ class MyEpAuthenticatorTest extends TestCase
// the flag would otherwise outlive the role it depends on
$this->assertFalse($user->isSuperAdmin());
$this->assertNotContains('ROLE_SUPER_ADMIN', $user->getRoles());
$this->assertNotContains('ROLE_TEAM_SUPER_ADMIN', $user->getRoles());
$this->assertSame(['HOTEL'], $user->getHotelCodes());
}
@@ -191,7 +191,7 @@ class MyEpAuthenticatorTest extends TestCase
$this->repository->method('findOneBy')->willReturn($user);
$this->entityManager->expects($this->never())->method('flush');
$userinfo = $this->createUserinfo(['ROLE_ADMIN', 'ROLE_TEAMER']);
$userinfo = $this->createUserinfo(['ROLE_TEAM_ADMIN', 'ROLE_TEAMER']);
$userinfo['profile']['hotel_codes'] = ['NEW'];
// returned rather than refused, so the UserChecker can explain the deletion
@@ -34,7 +34,7 @@ class DriverLicenseRequiredCheckTest extends TestCase
public function mixedRoleProvider(): array
{
return [
['ROLE_ADMIN'],
['ROLE_TEAM_ADMIN'],
['ROLE_MANAGER'],
['ROLE_HOUSE_MANAGER'],
];
@@ -33,7 +33,7 @@ class FinancialDataRequiredCheckTest extends TestCase
public function mixedRoleProvider(): array
{
return [
['ROLE_ADMIN'],
['ROLE_TEAM_ADMIN'],
['ROLE_MANAGER'],
['ROLE_HOUSE_MANAGER'],
];
@@ -33,7 +33,7 @@ class MealPreferenceRequiredCheckTest extends TestCase
public function mixedRoleProvider(): array
{
return [
['ROLE_ADMIN'],
['ROLE_TEAM_ADMIN'],
['ROLE_MANAGER'],
['ROLE_HOUSE_MANAGER'],
];
@@ -43,7 +43,7 @@ class PersonalDataVerificationRequiredCheckTest extends TestCase
public function mixedRoleProvider(): array
{
return [
['ROLE_ADMIN'],
['ROLE_TEAM_ADMIN'],
['ROLE_MANAGER'],
['ROLE_HOUSE_MANAGER'],
];
+1 -1
View File
@@ -75,7 +75,7 @@ class UserVoterTest extends TestCase
private function createUser(bool $superAdmin): User
{
return (new User())
->setRoles(['ROLE_ADMIN'])
->setRoles(['ROLE_TEAM_ADMIN'])
->setSuperAdmin($superAdmin)
;
}
@@ -97,14 +97,14 @@ class AccountDeletionHandlerTest extends TestCase
$teamer = new Teamer();
$user = (new User())
->setTeamer($teamer)
->setRoles(['ROLE_TEAMER', 'ROLE_ADMIN'])
->setRoles(['ROLE_TEAMER', 'ROLE_TEAM_ADMIN'])
;
$user->setDisabled(true);
$disabledAt = $user->getDisabledAt();
$this->handler->delete($user, AccountDeletionHandler::SOURCE_ADMIN);
$this->assertSame(['ROLE_TEAMER', 'ROLE_ADMIN'], $user->getAssignedRoles());
$this->assertSame(['ROLE_TEAMER', 'ROLE_TEAM_ADMIN'], $user->getAssignedRoles());
$this->assertSame($disabledAt, $user->getDisabledAt());
}