From 4fc059b2a59bf24652b19117ec52563c9a733569 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 11 Feb 2026 15:11:04 +0100 Subject: [PATCH] Update role logic for organization management --- src/Controller/OrganizationController.php | 131 ++++++--------------- src/Repository/OrganizationsRepository.php | 58 +++++---- 2 files changed, 70 insertions(+), 119 deletions(-) diff --git a/src/Controller/OrganizationController.php b/src/Controller/OrganizationController.php index b6d9a40..aa6d27f 100644 --- a/src/Controller/OrganizationController.php +++ b/src/Controller/OrganizationController.php @@ -95,14 +95,14 @@ class OrganizationController extends AbstractController try { $this->entityManager->persist($organization); $this->entityManager->flush(); - $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getId(), "Organization Created"); - $this->loggerService->logSuperAdmin($actingUser->getId(), $actingUser->getId(), "Organization Created", $organization->getId()); + $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Created"); + $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(), "Organization Created", $organization->getId()); $this->actionService->createAction("Create Organization", $actingUser, $organization, $organization->getName()); $this->addFlash('success', 'Organisation crée avec succès.'); return $this->redirectToRoute('organization_index'); } catch (Exception $e) { $this->addFlash('danger', 'Erreur lors de la création de l\'organization'); - $this->loggerService->logError('Error creating organization', ['acting_user_id' => $actingUser->getId(), 'error' => $e->getMessage()]); + $this->loggerService->logError('Error creating organization', ['acting_user_id' => $actingUser->getUserIdentifier(), 'error' => $e->getMessage()]); } } return $this->render('organization/new.html.twig', [ @@ -125,35 +125,12 @@ class OrganizationController extends AbstractController if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, - 'message' => 'Organization not found for edit'], $actingUser->getId() + 'message' => 'Organization not found for edit'], $actingUser->getUserIdentifier() ); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); return $this->redirectToRoute('organization_index'); } - if (!$this->isGranted("ROLE_SUPER_ADMIN")) { - //check if the user is admin of the organization - $uo = $this->entityManager->getRepository(UsersOrganizations::class)->findOneBy(['users' => $actingUser, 'organization' => $organization]); - if (!$uo) { - $this->loggerService->logEntityNotFound('UO link', [ - 'user_id' => $actingUser->getId(), - 'org_id' => $organization->getId(), - 'message' => 'UO link not found for edit organization' - ], $actingUser->getId()); - $this->addFlash('danger', 'Erreur, accès refusé.'); - return $this->redirectToRoute('organization_index'); - } - $roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']); - $uoaAdmin = $this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $uo, 'role' => $roleAdmin]); - if (!$uoaAdmin) { - $this->loggerService->logEntityNotFound('UOA link', [ - 'uo_id' => $uo->getId(), - 'role_id' => $roleAdmin->getId(), - 'message' => 'UOA link not found for edit organization, user is not admin of organization' - ], $actingUser->getId()); - $this->addFlash('danger', 'Erreur, accès refusé.'); - return $this->redirectToRoute('organization_index'); - } - } + $form = $this->createForm(OrganizationForm::class, $organization); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { @@ -164,16 +141,16 @@ class OrganizationController extends AbstractController try { $this->entityManager->persist($organization); $this->entityManager->flush(); - $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getId(), "Organization Edited"); + $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Edited"); if ($this->isGranted("ROLE_SUPER_ADMIN")) { - $this->loggerService->logSuperAdmin($actingUser->getId(), $actingUser->getId(), "Organization Edited", $organization->getId()); + $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(), "Organization Edited", $organization->getId()); } $this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName()); $this->addFlash('success', 'Organisation modifiée avec succès.'); return $this->redirectToRoute('organization_index'); }catch (Exception $e) { $this->addFlash('danger', 'Erreur lors de la modification de l\'organization'); - $this->loggerService->logError('Error editing organization', ['acting_user_id' => $actingUser->getId(), 'error' => $e->getMessage()]); + $this->loggerService->logError('Error editing organization', ['acting_user_id' => $actingUser->getUserIdentifier(), 'error' => $e->getMessage()]); } } return $this->render('organization/edit.html.twig', [ @@ -192,17 +169,18 @@ class OrganizationController extends AbstractController $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for view' - ], $actingUser->getId()); + ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); return $this->redirectToRoute('organization_index'); } //check if the user is admin of the organization if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) { - $this->loggerService->logAccessDenied($actingUser->getId()); + $this->loggerService->logAccessDenied($actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, accès refusé.'); throw new AccessDeniedHttpException('Access denied'); } + //TODO: add project to the response $allApps = $this->entityManager->getRepository(Apps::class)->findAll(); // appsAll $orgApps = $organization->getApps()->toArray(); // apps @@ -222,14 +200,14 @@ class OrganizationController extends AbstractController #[Route(path: '/delete/{id}', name: 'delete', methods: ['POST'])] public function delete($id): Response { - $this->denyAccessUnlessGranted("ROLE_ADMIN"); + $this->denyAccessUnlessGranted("ROLE_SUPER_ADMIN"); $actingUser = $this->getUser(); $organization = $this->organizationsRepository->find($id); if (!$organization) { $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for delete' - ], $actingUser->getId()); + ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } @@ -243,13 +221,13 @@ class OrganizationController extends AbstractController $this->entityManager->persist($organization); $this->actionService->createAction("Delete Organization", $actingUser, $organization, $organization->getName()); $this->entityManager->flush(); - $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getId(),'Organization Deleted'); + $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(),'Organization Deleted'); if ($this->isGranted("ROLE_SUPER_ADMIN")) { - $this->loggerService->logSuperAdmin($actingUser->getId(), $actingUser->getId(),'Organization Deleted', $organization->getId()); + $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization Deleted', $organization->getId()); } $this->addFlash('success', 'Organisation supprimée avec succès.'); }catch (\Exception $e){ - $this->loggerService->logError($actingUser->getId(), ['message' => 'Error deleting organization: '.$e->getMessage()]); + $this->loggerService->logError($actingUser->getUserIdentifier(), ['message' => 'Error deleting organization: '.$e->getMessage()]); $this->addFlash('danger', 'Erreur lors de la suppression de l\'organization.'); } @@ -266,16 +244,15 @@ class OrganizationController extends AbstractController $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for deactivate' - ], $actingUser->getId()); + ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } $organization->setIsActive(false); -// $this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, null, $organization); $this->entityManager->persist($organization); $this->actionService->createAction("Deactivate Organization", $actingUser, $organization, $organization->getName()); - $this->loggerService->logSuperAdmin($actingUser->getId(), $actingUser->getId(),'Organization deactivated', $organization->getId()); + $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization deactivated', $organization->getId()); $this->addFlash('success', 'Organisation désactivé avec succès.'); return $this->redirectToRoute('organization_index'); } @@ -290,14 +267,14 @@ class OrganizationController extends AbstractController $this->loggerService->logEntityNotFound('Organization', [ 'org_id' => $id, 'message' => 'Organization not found for activate' - ], $actingUser->getId()); + ], $actingUser->getUserIdentifier()); $this->addFlash('danger', 'Erreur, l\'organization est introuvable.'); throw $this->createNotFoundException(self::NOT_FOUND); } $organization->setIsActive(true); $this->entityManager->persist($organization); - $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getId(),'Organization Activated'); - $this->loggerService->logSuperAdmin($actingUser->getId(), $actingUser->getId(),'Organization Activated', $organization->getId()); + $this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(),'Organization Activated'); + $this->loggerService->logSuperAdmin($actingUser->getUserIdentifier(), $actingUser->getUserIdentifier(),'Organization Activated', $organization->getId()); $this->actionService->createAction("Activate Organization", $actingUser, $organization, $organization->getName()); $this->addFlash('success', 'Organisation activée avec succès.'); return $this->redirectToRoute('organization_index'); @@ -309,54 +286,21 @@ class OrganizationController extends AbstractController { $this->denyAccessUnlessGranted('ROLE_USER'); - - $page = max(1, (int)$request->query->get('page', 1)); - $size = max(1, (int)$request->query->get('size', 10)); - + $page = max(1, $request->query->getInt('page', 1)); + $size = max(1, $request->query->getInt('size', 10)); $filters = $request->query->all('filter'); + // Fetch paginated results + $paginator = $this->organizationsRepository->findAdmissibleOrganizations( + $this->getUser(), + $this->isGranted('ROLE_ADMIN'), // Super Admin check + $page, + $size, + $filters + ); - $qb = $this->organizationsRepository->createQueryBuilder('o') - ->where('o.isDeleted = :del')->setParameter('del', false); + $total = count($paginator); - if (!empty($filters['name'])) { - $qb->andWhere('o.name LIKE :name') - ->setParameter('name', '%' . $filters['name'] . '%'); - } - if (!empty($filters['email'])) { - $qb->andWhere('o.email LIKE :email') - ->setParameter('email', '%' . $filters['email'] . '%'); - } - if (!$this->isGranted('ROLE_ADMIN')) { - $actingUser = $this->getUser(); - $uo = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['users' => $actingUser]); - - $allowedOrgIds = []; - foreach ($uo as $item) { - if ($this->userService->isAdminOfOrganization($item->getOrganization())) { - $allowedOrgIds[] = $item->getOrganization()->getId(); - } - } - - // If user has no organizations, ensure query returns nothing (or handle typically) - if (empty($allowedOrgIds)) { - $qb->andWhere('1 = 0'); // Force empty result - } else { - $qb->andWhere('o.id IN (:orgIds)') - ->setParameter('orgIds', $allowedOrgIds); - } - } - - - // Count total - $countQb = clone $qb; - $total = (int)$countQb->select('COUNT(o.id)')->getQuery()->getSingleScalarResult(); - - // Pagination - $offset = ($page - 1) * $size; - $rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult(); - - // Map to array $data = array_map(function (Organizations $org) { return [ 'id' => $org->getId(), @@ -366,17 +310,12 @@ class OrganizationController extends AbstractController 'active' => $org->isActive(), 'showUrl' => $this->generateUrl('organization_show', ['id' => $org->getId()]), ]; - }, $rows); - - $lastPage = (int)ceil($total / $size); + }, iterator_to_array($paginator)); return $this->json([ 'data' => $data, - 'last_page' => $lastPage, - 'total' => $total, // optional, useful for debugging + 'last_page' => (int)ceil($total / $size), + 'total' => $total, ]); } - - - } diff --git a/src/Repository/OrganizationsRepository.php b/src/Repository/OrganizationsRepository.php index e598d9f..87d4772 100644 --- a/src/Repository/OrganizationsRepository.php +++ b/src/Repository/OrganizationsRepository.php @@ -3,8 +3,11 @@ namespace App\Repository; use App\Entity\Organizations; +use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; +use Doctrine\ORM\Tools\Pagination\Paginator; use Doctrine\Persistence\ManagerRegistry; +use App\Entity\UsersOrganizations; /** * @extends ServiceEntityRepository @@ -16,28 +19,37 @@ class OrganizationsRepository extends ServiceEntityRepository parent::__construct($registry, Organizations::class); } - // /** - // * @return Organizations[] Returns an array of Organizations objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('o') - // ->andWhere('o.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('o.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } + public function findAdmissibleOrganizations(User $user, bool $isSuperAdmin, int $page, int $size, array $filters = []): Paginator + { + $qb = $this->createQueryBuilder('o') + ->where('o.isDeleted = :del') + ->setParameter('del', false); - // public function findOneBySomeField($value): ?Organizations - // { - // return $this->createQueryBuilder('o') - // ->andWhere('o.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } + // 1. Security Logic: If not Super Admin, join UsersOrganizations to filter + if (!$isSuperAdmin) { + $qb->innerJoin(UsersOrganizations::class, 'uo', 'WITH', 'uo.organization = o') + ->andWhere('uo.users = :user') + ->andWhere('uo.role = :roleAdmin') + ->andWhere('uo.isActive = true') + ->setParameter('user', $user) + // You can pass the actual Role entity or the string name depending on your mapping + ->setParameter('roleAdmin', $this->_em->getRepository(\App\Entity\Roles::class)->findOneBy(['name' => 'ADMIN'])); + } + + // 2. Filters + if (!empty($filters['name'])) { + $qb->andWhere('o.name LIKE :name') + ->setParameter('name', '%' . $filters['name'] . '%'); + } + if (!empty($filters['email'])) { + $qb->andWhere('o.email LIKE :email') + ->setParameter('email', '%' . $filters['email'] . '%'); + } + + // 3. Pagination + $qb->setFirstResult(($page - 1) * $size) + ->setMaxResults($size); + + return new Paginator($qb); + } }