diff --git a/src/Controller/OrganizationController.php b/src/Controller/OrganizationController.php index 4c0977b..a8be762 100644 --- a/src/Controller/OrganizationController.php +++ b/src/Controller/OrganizationController.php @@ -30,206 +30,5 @@ class OrganizationController extends AbstractController { } - #[Route('/', name: 'index', methods: ['GET'])] - public function index(): Response - { - if ($this->isGranted('ROLE_SUPER_ADMIN')) { - $organizations = $this->entityManager->getRepository(Organizations::class)->findBy(['isDeleted' => false]); - } else { - $user = $this->getUser(); - if (!$user) { - return $this->redirectToRoute('app_login'); - } - $userIdentifier = $user->getUserIdentifier(); - - $organizations = $this->entityManager->getRepository(UsersOrganizations::class)->findOrganizationsByUserEmailAndRoleName($userIdentifier, 'ADMIN'); - if (!$organizations) { -// if user is not admin in any organization, throw access denied - throw $this->createNotFoundException(self::ACCESS_DENIED); - } - } - - return $this->render('organization/index.html.twig', [ - 'organizations' => $organizations, - ]); - } - - #[Route('/new', name: 'new', methods: ['GET', 'POST'])] - public function new(Request $request): Response - { - if (!$this->isGranted('ROLE_SUPER_ADMIN')) { - throw $this->createNotFoundException(self::ACCESS_DENIED); - } - $form = $this->createForm(OrganizationForm::class); - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $organization = $form->getData(); - $logoFile = $form->get('logoUrl')->getData(); - - if ($logoFile) { - $currentDate = (new \DateTime())->format('Y-m-d'); - $organizationName = preg_replace('/[^a-zA-Z0-9]/', '_', $organization->getName()); - $extension = $logoFile->guessExtension(); - $newFilename = $currentDate . '_' . $organizationName . $extension; - // Move the file to the directory where logos are stored - $logoFile->move( - $this->getParameter('logos_directory'), - $newFilename - ); - - // Update the 'logoUrl' property to store the file name - $organization->setLogoUrl($newFilename); - } - $this->entityManager->persist($organization); - $this->entityManager->flush(); - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Création d'une organisation",$user, null, "{$user->getUserIdentifier()} a crée l'organisation {$organization->getName()}"); - $this->addFlash('success', 'Organization created successfully'); - return $this->redirectToRoute('organization_index'); - } - return $this->render('organization/new.html.twig', [ - 'form' => $form->createView(), - ]); - } - - #[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])] - public function show(int $id, ActionService $actionService): Response - { - if ($this->isGranted('ROLE_ADMIN')) { - $user = $this->getUser(); - if (!$user) { - return $this->redirectToRoute('app_login'); - } - - //Don't care about the null pointer because if no UO found, it won't pass the previous check - $organization = $this->entityManager->getRepository(Organizations::class)->find($id); - $newUsers = $this->entityManager->getRepository(UsersOrganizations::class)->getLastNewActiveUsersByOrganization($organization); - $adminUsers = $this->entityManager->getRepository(UsersOrganizations::class)->getAdminUsersByOrganization($organization); -// reusing the method to avoid code duplication even though it returns an array of UsersOrganizations - $org = $this->usersOrganizationService->findActiveUsersByOrganizations([$organization]); - -// get all applications - $applications = $this->organizationsService->getApplicationsWithAccessStatus($organization); - - $actions = $organization->getActions()->toArray(); - usort($actions, static function ($a, $b) { - return $b->getDate() <=> $a->getDate(); - }); - //get the last 10 activities - $actions = array_slice($actions, 0, 10); - $activities = array_map(static function ($activity) use ($actionService) { - return [ - 'date' => $activity->getDate(), // or however you access the date - 'actionType' => $activity->getActionType(), - 'users' => $activity->getUsers(), - 'color' => $actionService->getActivityColor($activity->getDate()) - ]; - }, $actions); - } else { - throw $this->createNotFoundException(self::ACCESS_DENIED); - } - - return $this->render('organization/show.html.twig', [ - 'organization' => $organization, - 'adminUsers' => $adminUsers, - 'newUsers' => $newUsers, - 'org' => !empty($org) ? $org[0] : null, - 'applications' => $applications, - 'activities' => $activities - ]); - } - - #[Route('/edit/{id}', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])] - public function edit(Request $request): Response - { - $id = $request->attributes->get('id'); - if (!$this->isGranted('ROLE_ADMIN')) { - throw $this->createNotFoundException(self::ACCESS_DENIED); - } - $organization = $this->entityManager->getRepository(Organizations::class)->find($id); - if (!$organization) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - $form = $this->createForm(OrganizationForm::class, $organization); - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - $logoFile = $form->get('logoUrl')->getData(); - - if ($logoFile) { - $currentDate = (new \DateTime())->format('Y-m-d'); - $organizationName = preg_replace('/[^a-zA-Z0-9]/', '_', $organization->getName()); - $extension = $logoFile->guessExtension(); - $newFilename = $currentDate . '_' . $organizationName . '.' . $extension; - // Move the file to the directory where logos are stored - $logoFile->move( - $this->getParameter('logos_directory'), - $newFilename - ); - - // Update the 'logoUrl' property to store the file name - $organization->setLogoUrl($newFilename); - } - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Modification d'une organisation",$user, $organization, "{$user->getUserIdentifier()} a modifié l'organisation {$organization->getName()}"); - $this->entityManager->persist($organization); - $this->entityManager->flush(); - $this->addFlash('success', 'Organization updated successfully'); - return $this->redirectToRoute('organization_index'); - } - return $this->render('organization/edit.html.twig', [ - 'form' => $form->createView(), - 'organization' => $organization, - ]); - } - - #[Route('/deactivate/{id}', name: 'deactivate', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])] - public function deactivate(Request $request): Response - { - $this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN'); - $id = $request->attributes->get('id'); - $organization = $this->entityManager->getRepository(Organizations::class)->find($id); - if (!$organization) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - if ($organization->isActive() === false) { - $this->addFlash('error', 'Organization is already deactivated'); - return $this->redirectToRoute('organization_index'); - } - - $organization->setIsActive(false); - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Création d'une organisation",$user, $organization, "{$user->getUserIdentifier()} a désactivé l'organisation {$organization->getName()}"); - $this->entityManager->persist($organization); - $this->entityManager->flush(); - $this->addFlash('success', 'Organization deactivated successfully'); - return $this->redirectToRoute('organization_index'); - } - - #[Route('/activate/{id}', name: 'activate', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])] - public function activate(Request $request): Response - { - $this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN'); - $id = $request->attributes->get('id'); - $organization = $this->entityManager->getRepository(Organizations::class)->find($id); - if (!$organization) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - if ($organization->isActive() === true) { - $this->addFlash('error', 'Organization is already active'); - return $this->redirectToRoute('organization_index'); - } - - $organization->setIsActive(true); - $this->entityManager->persist($organization); - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Création d'une organisation",$user, $organization, "{$user->getUserIdentifier()} a activé l'organisation {$organization->getName()}"); - $this->entityManager->flush(); - $this->addFlash('success', 'Organization activated successfully'); - return $this->redirectToRoute('organization_index'); - } } diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 0774433..7b25855 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -32,385 +32,4 @@ class UserController extends AbstractController { } - /** - * GET /user - List all users (index/collection) - */ - #[Route('/', name: 'index', methods: ['GET'])] - public function index(EntityManagerInterface $entityManager): Response - { - if ($this->isGranted('ROLE_SUPER_ADMIN')) { - $usersByOrganization = $this->userOrganizationService->getActiveUsersGroupedByOrganization(); - } else { - $user = $this->getUser(); - if (!$user) { - return $this->redirectToRoute('app_login'); - } - $userIdentifier = $user->getUserIdentifier(); - $organizations = $this->entityManager->getRepository(UsersOrganizations::class)->findOrganizationsByUserEmailAndRoleName($userIdentifier, 'ADMIN'); - if (!$organizations) { -// if user is not admin in any organization, throw access denied - throw $this->createNotFoundException(self::ACCESS_DENIED); - } - $usersByOrganization = $this->userOrganizationService->findActiveUsersByOrganizations($organizations); - } - return $this->render('user/index.html.twig', [ - 'usersByOrganization' => $usersByOrganization, - 'controller_name' => 'IndexController', - ]); - } - - /** - * GET /user/{id} - Show specific user (show/member) - */ - #[Route('/{id}', name: 'show', requirements: ['id' => '\d+'], methods: ['GET'])] - public function show(int $id, EntityManagerInterface $entityManager, Request $request): Response - { - - $user = $entityManager->getRepository(User::class)->find($id); - if (!$user) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - if ($request->query->has('organizationId')) { - $userOrganizations = $this->userOrganizationService->getUserOrganizations($user, $request->query->get('organizationId')); - } else { - $userOrganizations = $this->userOrganizationService->getUserOrganizations($user); - } - - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - - $isSameUser = $user->getUserIdentifier() === $actingUser->getUserIdentifier(); - $isAdminOrg = false; - foreach ($userOrganizations as $userOrganization) { - $organization = $userOrganization['organization']; - if ($this->userService->isUserAdminInOrganization($actingUser->getId(), $organization->getId())) { - $isAdminOrg = true; - break; - } - } - if (!$this->isGranted('ROLE_SUPER_ADMIN') && - !$isSameUser && - !$isAdminOrg) { - throw $this->createAccessDeniedException(self::ACCESS_DENIED); - } - - return $this->render('user/show.html.twig', [ - 'user' => $user, - 'userOrganizations' => $userOrganizations, - ]); - } - - /** - * GET /user/new - Show form to create new user and handle submission - */ - #[Route('/new', name: 'new', methods: ['GET', 'POST'])] - public function new(Request $request): Response - { - $this->denyAccessUnlessGranted("ROLE_ADMIN"); - $form = $this->createForm(UserForm::class); - $organizationId = $request->query->get('organizationId'); - - $form->handleRequest($request); - - if ($form->isSubmitted() && $form->isValid()) { - //Data is a User object. App\Form\NewUserForm is a form type that maps to User entity - $data = $form->getData(); - // Handle user creation logic here - - - //FOR DEV PURPOSES ONLY - $data->setPictureUrl(""); - $data->setPassword($this->userService->generateRandomPassword()); - //FOR DEV PURPOSES ONLY - $orgId = $request->get('organization_id'); - if ($orgId) { - $organization = $this->entityManager->getRepository(Organizations::class)->find($orgId); - $roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']); - if (!$organization || !$roleUser) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - $uo = new UsersOrganizations(); - $uo->setOrganization($organization); - $uo->setRole($roleUser); - $uo->setUsers($data); - - //log the action - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Création d'un utilisateur", $user, $organization, "{$user->getIdentifier()} à ajouter l'utilisateur {$data->getUserIdentifier()} à l'organisation {$organization->getName()}"); - $this->entityManager->persist($uo); - } else { - $user = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $user->getUserIdentifier()]); - $this->actionService->createAction("Création d'un utilisateur", $user, null, "{$user->getIdentifier()} à ajouter l'utilisateur {$data->getUserIdentifier()} sans organisation"); - } - $this->entityManager->persist($data); - - - $this->entityManager->flush(); - - // Redirect to user index - return $this->redirectToRoute('user_index'); - } - - return $this->render('user/new.html.twig', [ - 'form' => $form->createView(), - 'organizationId' => $organizationId, - ]); - } - - /** - * GET /user/{id}/edit - Show form to edit user - */ - #[Route('/edit/{id}', name: 'edit', requirements: ['id' => '\d+'], methods: ['GET', 'PUT', 'POST'])] - public function edit(int $id, EntityManagerInterface $entityManager, Request $request): Response - { - // Fetch user by ID and handle not found case - $user = $entityManager->getRepository(User::class)->find($id); - if (!$user) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - - // Get the acting user (the one making the request) - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - - // Check if acting user is the same as the user being edited - $isSameUser = $user->getUserIdentifier() === $actingUser->getUserIdentifier(); - - // Get all organizations of the user being edited - $userOrganizations = $this->userOrganizationService->getUserOrganizations($user); - - // Check if acting user is admin in any of the user's organizations - $isAdminOrg = false; - foreach ($userOrganizations as $userOrganization) { - $organization = $userOrganization['organization']; - if ($this->userService->isUserAdminInOrganization($actingUser->getId(), $organization->getId())) { - $isAdminOrg = true; - break; - } - } - - // Access control: allow if super admin, same user, or admin in org - if ( - !$this->isGranted('ROLE_SUPER_ADMIN') && - !$isSameUser && - !$isAdminOrg - ) { - throw $this->createAccessDeniedException(self::ACCESS_DENIED); - } - - // Create form for editing user - $form = $this->createForm(UserForm::class, $user); - - // Handle form submission - $form->handleRequest($request); - if ($form->isSubmitted() && $form->isValid()) { - // Persist changes to the user entity - $entityManager->persist($user); - - // Log the action - $this->actionService->createAction( - "Modification d'un utilisateur", - $actingUser, - null, - "{$actingUser->getIdentifier()} a modifié l'utilisateur {$user->getUserIdentifier()}" - ); - $entityManager->flush(); - - // Redirect to user profile after successful edit - return $this->redirectToRoute('user_show', ['id' => $user->getId()]); - } - - return $this->render('user/edit.html.twig', [ - 'form' => $form->createView(), - 'user' => $user, - ]); - } - - - /** - * DELETE /user/{id} - Delete user - */ - #[Route('/{id}', name: 'setDelete', requirements: ['id' => '\d+'], methods: ['POST'])] - public function setDelete(int $id, EntityManagerInterface $entityManager): Response - { - //This method is used to set a user as deleted without actually removing them from the database. - - //Handle access control - if (!$this->isGranted('ROLE_SUPER_ADMIN')) { - throw $this->createAccessDeniedException(self::ACCESS_DENIED); - } - //Fetch user by ID and handle not found case - $user = $entityManager->getRepository(User::class)->find($id); - if (!$user) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - - // Handle user deletion logic - $user->setIsDeleted(true); - $entityManager->persist($user); - // Log the action - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - $this->actionService->createAction("Supression d'un utilisateur", $actingUser, null, "{$actingUser->getIdentifier()} a supprimé l'utilisateur {$user->getUserIdentifier()}"); - $entityManager->flush(); - - return $this->redirectToRoute('user_index'); - } - - /** - * DELETE /user/{id} - Delete user - */ - #[Route('/{id}', name: 'delete', requirements: ['id' => '\d+'], methods: ['DELETE'])] - public function delete(int $id, EntityManagerInterface $entityManager): Response - { - if (!$this->isGranted('ROLE_SUPER_ADMIN')) { - throw $this->createAccessDeniedException(self::ACCESS_DENIED); - } - - $user = $entityManager->getRepository(User::class)->find($id); - if (!$user) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - - // Handle user deletion logic - $entityManager->remove($user); - // Log the action - - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - $this->actionService->createAction("Suppression définitive d'un utilisateur", $actingUser, null, "{$actingUser->getIdentifier()} a supprimé l'utilisateur {$user->getUserIdentifier()}"); - $entityManager->flush(); - - return $this->redirectToRoute('user_index'); - } - - - /** - * GET /user/deactivate/{id} - Deactivate user - * This method is used to deactivate a user without deleting them. - * The user will still exist in the database but will not be active. - */ - #[Route('/deactivate/{id}', name: 'deactivate', methods: ['GET'])] - public function deactivate(Request $request, EntityManagerInterface $entityManager): Response - { - if ($this->isGranted('ROLE_SUPER_ADMIN')) { - $userId = $request->attributes->get('id'); - $user = $entityManager->getRepository(User::class)->find($userId); - if (!$user) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - $user->setIsActive(false); - $entityManager->persist($user); - // Log the action - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - $this->actionService->createAction("Désactivation d'un utilisateur", $actingUser, null, "{$actingUser->getIdentifier()} a désactivé l'utilisateur {$user->getUserIdentifier()}"); - $entityManager->flush(); - return $this->redirectToRoute('user_index'); - } - return new Response('Unauthorized', Response::HTTP_UNAUTHORIZED); - } - - /** - * Update organization user /userOrganizationEdit/{id} - Update organization user - * The id parameter is the ID of the UsersOrganizations entity. - */ - #[Route('/userOrganizationEdit/{id}', name: 'organization_edit', requirements: ['id' => '\d+'], methods: ['GET', 'POST'])] - public function userOrganizationEdit(int $id, Request $request, EntityManagerInterface $entityManager, Packages $packages): Response - { - $this->denyAccessUnlessGranted('ROLE_ADMIN'); - -// get the UsersOrganizations entity by ID and handle not found case same for user - $userOrganization = $entityManager->getRepository(UsersOrganizations::class)->find($id); - if (!$userOrganization) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - $user = $userOrganization->getUsers() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $organization = $userOrganization->getOrganization() ?? throw $this->createNotFoundException(self::NOT_FOUND); - - //Handle the POST - if ($request->isMethod('POST')) { - // Get the selected roles and apps from the request - $selectedRoles = $request->request->all('roles'); - $selectedApps = $request->request->all('applications'); - -// order in important here. apps MUST be before roles - $this->userOrganizationService->setUserOrganizationsApps($user, $organization, $selectedApps); - $this->userOrganizationService->setUserOrganizations($user, $organization, $selectedRoles); - - - // Redirect to the user profile after successful update - return $this->redirectToRoute('user_show', ['id' => $user->getId()]); - } - - //Overwrite the userOrganization with the userOrganizationsService for data consistency - // NULL pointer won't occur here because a valid UsersOrganizations entity was fetched above - $userOrganization = $this->userOrganizationService->getUserOrganizations($userOrganization->getUsers(), $userOrganization->getOrganization()->getId()); - - // Fetch all roles and apps - $roles = $entityManager->getRepository(Roles::class)->findAll(); - $apps = $organization->getApps() ?? throw $this->createNotFoundException(self::NOT_FOUND); - if (!$roles) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - if (!$apps) { - throw $this->createNotFoundException(self::NOT_FOUND); - } - - // Map roles and apps to arrays for rendering - $rolesArray = array_map(static function ($role) { - return [ - 'id' => $role->getId(), - 'name' => $role->getName() - ]; - }, $roles); - $appsArray = []; - foreach ($apps as $app) { - $appsArray[] = [ - 'id' => $app->getId(), - 'name' => $app->getName(), - 'icon' => $packages->getUrl($app->getLogoUrl()), - ]; - } - - // Map selected roles and apps to their IDs for the form - $selectedRoles = array_map(static function ($role) { - return $role->getId(); - }, $userOrganization[0]["roles"]); - $selectedApps = array_map(static function ($app) { - return $app->getId(); - }, $userOrganization[0]["apps"]); - - return $this->render('user/organization/edit.html.twig', [ - 'userOrganization' => $userOrganization, - 'user' => $user, - 'rolesArray' => $rolesArray, - 'selectedRoleIds' => $selectedRoles, - 'appsArray' => $appsArray, - 'selectedAppIds' => $selectedApps,]); - } - - /** - * GET /user/deactivateOrganization/{id} - Deactivate user - * This method is used to deactivate a user without deleting them in an organization. - * The user will still exist in the database but will not be active. - */ - #[Route('/organizationDeactivate/{id}', name: 'organization_deactivate', requirements: ['id' => '\d+'], methods: ['GET'])] - public function deactivateUserOrganization(int $id, Request $request, EntityManagerInterface $entityManager): Response - { - $this->denyAccessUnlessGranted('ROLE_ADMIN'); - $userOrganization = $entityManager->getRepository(UsersOrganizations::class)->find($id) ?? throw $this->createNotFoundException(self::NOT_FOUND); - $user = $userOrganization->getUsers() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $organization = $userOrganization->getOrganization() ?? throw $this->createNotFoundException(self::NOT_FOUND); - - $this->userOrganizationService->deactivateAllUserRoles($user, $organization); - $actingUser = $this->getUser() ?? throw $this->createNotFoundException(self::NOT_FOUND); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser->getUserIdentifier()]); - $this->actionService->createAction("Désactivation d'un utilisateur dans une organisation", $actingUser, $organization, "{$actingUser->getIdentifier()} a désactivé l'utilisateur {$user->getUserIdentifier()} dans l'organisation {$organization->getName()}"); - - return $this->redirectToRoute('user_show', ['id' => $user->getId()]); - } - } diff --git a/src/Form/UserOrganization.php b/src/Form/UserOrganization.php deleted file mode 100644 index d4fe7b9..0000000 --- a/src/Form/UserOrganization.php +++ /dev/null @@ -1,27 +0,0 @@ -add('admin' , CheckboxType::class, [ - 'label' => 'Admin', - 'required' => false]) - ->add('application' , ChoiceType::class, [ - 'label' => 'Application', - 'choices' => [ - 'Application 1' => 'app1', - 'Application 2' => 'app2', - 'Application 3' => 'app3', - ]]); - } - -} diff --git a/src/Repository/UsersOrganizationsRepository.php b/src/Repository/UsersOrganizationsRepository.php index bedbc5b..6f4da99 100644 --- a/src/Repository/UsersOrganizationsRepository.php +++ b/src/Repository/UsersOrganizationsRepository.php @@ -20,115 +20,4 @@ class UsersOrganizationsRepository extends ServiceEntityRepository } - /** - * Find organizations where a user has a specific role (e.g., ADMIN) - * - * @param string $userEmail - * @param string $roleName - * @return array Array of UsersOrganizations entities (with organizations preloaded) - */ - public function findUserOrganizationsByRole(string $userEmail, string $roleName): array - { - return $this->createQueryBuilder('uo') - ->select('uo, o') - ->innerJoin('uo.users', 'u') - ->innerJoin('uo.organization', 'o') - ->innerJoin('uo.userOrganizatonApps', 'uoa') - ->innerJoin('uoa.role', 'r') - ->where('u.email = :email') - ->andWhere('r.name = :roleName') - ->andWhere('uo.isActive = :isActive') - ->andWhere('u.isActive = :userIsActive') - ->andWhere('o.isActive = :orgIsActive') - ->andWhere('uoa.isActive = :uoaIsActive') - ->setParameter('email', $userEmail) - ->setParameter('roleName', $roleName) - ->setParameter('isActive', true) - ->setParameter('userIsActive', true) - ->setParameter('orgIsActive', true) - ->setParameter('uoaIsActive', true) - ->getQuery() - ->getResult(); - } - - /** - * Get all active user-organization relationships. - * Optionally filter by specific organizations. - * - * @param array|null $organizations Array of Organization entities to filter by - * @return array Array of UsersOrganizations entities - */ - public function getAllActiveUserOrganizationLinks(array $organizations = null): array - { - $queryBuilder = $this->createQueryBuilder('uo') - ->innerJoin('uo.users', 'u') - ->innerJoin('uo.organization', 'o') - ->where('uo.isActive = :isActive') - ->andWhere('u.isActive = :userIsActive') - ->andWhere('u.isDeleted = :userIsDeleted') - ->andWhere('o.isActive = :orgIsActive') - ->andWhere('o.isDeleted = :orgIsDeleted') - ->setParameter('isActive', true) - ->setParameter('userIsActive', true) - ->setParameter('userIsDeleted', false) - ->setParameter('orgIsActive', true) - ->setParameter('orgIsDeleted', false) - ->orderBy('o.name', 'ASC') - ->addOrderBy('u.surname', 'ASC'); - - // Filter by specific organizations if provided - if ($organizations !== null && !empty($organizations)) { - $queryBuilder->andWhere('uo.organization IN (:organizations)') - ->setParameter('organizations', $organizations); - } - - return $queryBuilder->getQuery()->getResult(); - } - - /** - * Get all active users who are not in any organization. - * - * @return array Array of User entities - */ - public function getUsersWithoutOrganizations(): array - { - return $this->getEntityManager()->getRepository(User::class) - ->createQueryBuilder('u') - ->leftJoin('App\Entity\UsersOrganizations', 'uo', 'WITH', 'uo.users = u AND uo.isActive = true') - ->where('u.isActive = :userIsActive') - ->andWhere('u.isDeleted = :userIsDeleted') - ->andWhere('uo.id IS NULL') - ->setParameter('userIsActive', true) - ->setParameter('userIsDeleted', false) - ->orderBy('u.surname', 'ASC') - ->getQuery() - ->getResult(); - } - -// /** -// * Get the last 10 new active users for a specific organization. -// * Users are ordered by creation date (most recent first). -// * -// * @param Organizations $organization -// * @return array -// */ -// public function getLastNewActiveUsersByOrganization(Organizations $organization): array -// { -// return $this->createQueryBuilder('uo') -// ->select('u.id', 'u.surname', 'u.name', 'u.email', 'u.pictureUrl', 'u.isActive', 'uo.createdAt') -// ->innerJoin('uo.users', 'u') -// ->innerJoin('uo.organization', 'o') -// ->where('uo.isActive = :isActive') -// ->andWhere('u.isActive = :userIsActive') -// ->andWhere('o.isActive = :orgIsActive') -// ->andWhere('uo.organization = :organization') -// ->setParameter('isActive', true) -// ->setParameter('userIsActive', true) -// ->setParameter('orgIsActive', true) -// ->setParameter('organization', $organization) -// ->orderBy('uo.createdAt', 'DESC') -// ->setMaxResults(10) -// ->getQuery() -// ->getResult(); -// } -} + } diff --git a/src/Service/CguUserService.php b/src/Service/CguUserService.php index cd104ef..279c33e 100644 --- a/src/Service/CguUserService.php +++ b/src/Service/CguUserService.php @@ -54,7 +54,7 @@ class CguUserService //Function can only be called if the user has already accepted the CGU public function declineCgu(UserInterface $user, Cgu $cgu): void { - $cguUser = $this->entityManager->getRepository(CguUser::class)->findOneBy(['users' => $user, 'cgu' => $cgu]); + $cguUser = $this->entityManager->getRepository(CguUser::class)->findOneBy(['users' => $user, 'cgu' => $cgu])?? throw new \InvalidArgumentException('CGU not found for this user'); $cguUser->setIsAccepted(false); $this->entityManager->flush(); diff --git a/src/Service/OrganizationsService.php b/src/Service/OrganizationsService.php index 362d850..b98d3ab 100644 --- a/src/Service/OrganizationsService.php +++ b/src/Service/OrganizationsService.php @@ -14,48 +14,4 @@ class OrganizationsService { } - public function getAdminUsers(Organizations $organization): array{ - $roleAdmin = $this->entityManager->getRepository(Roles::class)->findBy(['name' => 'ADMIN']); - return $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['organization'=>$organization, - 'role' => $roleAdmin, - 'isActive' => true]); - } - - public function getNewUsers(Organizations $organization): array{ - return $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['organization'=>$organization, - 'isActive' => true]); - } - - /** - * Get all applications with organization access status - * - * @param Organizations $organization - * @return array - */ - public function getApplicationsWithAccessStatus(Organizations $organization): array - { - // Get all applications - $allApplications = $this->entityManager->getRepository(Apps::class)->findAll(); - - // Get applications the organization has access to - $organizationApps = $organization->getApps(); - - // Create a lookup array for faster checking - $orgAppIds = []; - foreach ($organizationApps as $app) { - $orgAppIds[$app->getId()] = true; - } - - // Build result array - $result = []; - foreach ($allApplications as $app) { - $result[] = [ - 'application' => $app, - 'has_access' => isset($orgAppIds[$app->getId()]) - ]; - } - - return $result; - } - } \ No newline at end of file diff --git a/src/Service/UserOrganizationService.php b/src/Service/UserOrganizationService.php index b2d37e6..6ea9700 100644 --- a/src/Service/UserOrganizationService.php +++ b/src/Service/UserOrganizationService.php @@ -19,474 +19,6 @@ use Symfony\Bundle\SecurityBundle\Security; */ readonly class UserOrganizationService { - /** - * Constructeur du service UserOrganizationService. - * - * @param EntityManagerInterface $entityManager Le gestionnaire d'entités Doctrine - */ - public function __construct(private readonly EntityManagerInterface $entityManager, - private readonly UserService $userService, - private ActionService $actionService, - private readonly Security $security) - { - } - - /** - * Récupère toutes les organisations auxquelles appartient l'utilisateur donné, - * incluant les rôles et applications uniques de l'utilisateur dans chaque organisation. - * - * @param User $user L'utilisateur concerné - * @param int|null $organizationsId ID optionnel pour filtrer par organisation - * @return array - */ - public function getUserOrganizations(User $user, int $organizationsId = null): array - { - $userOrganizations = $this->entityManager - ->getRepository(UsersOrganizations::class) - ->findAllDistinctOrganizationsByUserId($user->getId()); - $organizations = []; - - foreach ($userOrganizations as $uo) { - $orgId = $uo->getOrganization()->getId(); - - // Si $organizationsId est fourni, ignorer les autres organisations - if ($organizationsId !== null && $orgId !== $organizationsId) { - continue; - } - - // Initialiser l'entrée de l'organisation si elle n'existe pas - $organizations[$orgId] = $organizations[$orgId] ?? $this->createEmptyOrganizationBucket($uo); - $organizations[$orgId]['uoId'] = $uo->getId(); - - // Agréger les rôles et applications - $this->addRole($organizations[$orgId]['roles'], $uo->getRole()); - $this->addApps($organizations[$orgId]['apps'], $uo->getApps()); - } - - // Ordonner les rôles : Super Admin, Admin, puis les autres - foreach ($organizations as &$org) { - $org['roles'] = $this->sortRoles($org['roles']); - } - unset($org); - - $this->normalizeAppsIndexes($organizations); - - return array_values($organizations); - } - - /** - * Trie les rôles pour que Super Admin et Admin soient en premier, puis les autres. - * - * @param Roles[] $roles - * @return Roles[] - */ - private function sortRoles(array $roles): array - { - usort($roles, function ($a, $b) { - $priority = [ - 'SUPER_ADMIN' => 0, - 'ADMIN' => 1 - ]; - $aName = strtoupper($a->getName()); - $bName = strtoupper($b->getName()); - $aPriority = $priority[$aName] ?? 2; - $bPriority = $priority[$bName] ?? 2; - if ($aPriority === $bPriority) { - return strcmp($aName, $bName); - } - return $aPriority <=> $bPriority; - }); - return $roles; - } - - /** - * Initialise la structure de données pour une organisation. - * - * @param UsersOrganizations $link Lien utilisateur-organisation - * @return array{organization:object, roles:Roles[], apps:array} - */ - private function createEmptyOrganizationBucket(UsersOrganizations $link): array - { - return [ - 'organization' => $link->getOrganization(), - 'roles' => [], - 'apps' => [], - ]; - } - - /** - * Ajoute un rôle à la liste s'il n'est pas déjà présent (par ID). - * - * @param Roles[] &$roles Liste des rôles - * @param Roles|null $role Rôle à ajouter - * @return void - */ - private function addRole(array &$roles, ?Roles $role): void - { - if ($role === null) { - return; - } - foreach ($roles as $existingRole) { - if ($existingRole->getId() === $role->getId()) { - return; // Already present - } - } - $roles[] = $role; - } - - /** - * Fusionne une ou plusieurs applications dans le tableau associatif, une entrée par ID. - * - * @param array &$apps Tableau des applications - * @param iterable $appsToAdd Applications à ajouter - * @return void - */ - private function addApps(array &$apps, iterable $appsToAdd): void - { - foreach ($appsToAdd as $app) { - $apps[$app->getId()] = $apps[$app->getId()] ?? $app; - } - } - - /** - * Normalise le tableau des applications pour le rendre indexé (JSON-friendly). - * - * @param array &$organizations Tableau des organisations - * @return void - */ - private function normalizeAppsIndexes(array &$organizations): void - { - foreach ($organizations as &$org) { - $org['apps'] = array_values($org['apps']); - } - } - - /** - * Définit les rôles d'un utilisateur dans une organisation, en s'assurant que le rôle USER est toujours présent. - * Désactive tous les rôles si USER n'est pas sélectionné. - * - * @param User $user L'utilisateur - * @param Organizations $organization L'organisation - * @param array $selectedRoles Tableau des IDs de rôles sélectionnés - * @return void - * @throws \RuntimeException Si le rôle USER n'est pas trouvé - */ - public function setUserOrganizations(User $user, Organizations $organization, array $selectedRoles): void - { - $repo = $this->entityManager->getRepository(UsersOrganizations::class); - $roleRepo = $this->entityManager->getRepository(Roles::class); - $userRole = $roleRepo->findOneBy(['name' => 'USER']); - if (!$userRole) { - throw new \RuntimeException('USER role not found'); - } - if (!in_array($userRole->getId(), $selectedRoles)) { - $this->deactivateAllUserRoles($user, $organization); - return; - } - $currentUserOrgs = $repo->findBy([ - 'users' => $user, - 'organization' => $organization - ]); - $currentRolesMap = $this->mapUserOrgRoles($currentUserOrgs); - $selectedRoles = $this->ensureUserRolePresent($selectedRoles, $userRole->getId()); - $this->addOrUpdateRoles($selectedRoles, $currentRolesMap, $roleRepo, $user, $organization); - $this->deactivateUnselectedRoles($currentRolesMap); - $this->entityManager->flush(); - } - - /** - * Met à jour les applications associées à l'utilisateur dans une organisation. - * - * @param User $user L'utilisateur - * @param Organizations $organization L'organisation - * @param array $selectedApps Tableau des IDs d'applications sélectionnées - * @return void - */ - public function setUserOrganizationsApps(User $user, Organizations $organization, array $selectedApps): void - { - $roleUser = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'USER']); - $uoEntity = $this->entityManager - ->getRepository(UsersOrganizations::class) - ->findOneBy(['users' => $user, 'organization' => $organization, 'role' => $roleUser]); - if (!$uoEntity) { - return; - } - $this->removeUnselectedApps($uoEntity, $selectedApps); - $this->addSelectedApps($uoEntity, $selectedApps); - $this->entityManager->persist($uoEntity); - $this->entityManager->flush(); - } - - /** - * Crée une map des rôles actuels de l'utilisateur dans l'organisation. - * @param array $currentUserOrgs - * @return array - */ - private function mapUserOrgRoles(array $currentUserOrgs): array - { - $map = []; - foreach ($currentUserOrgs as $uo) { - $map[$uo->getRole()->getId()] = $uo; - } - return $map; - } - - /** - * S'assure que le rôle USER est présent dans la sélection si nécessaire. - * @param array $selectedRoles - * @param int $userRoleId - * @return array - */ - private function ensureUserRolePresent(array $selectedRoles, int $userRoleId): array - { - $hasNonUserRole = false; - foreach ($selectedRoles as $roleId) { - if ($roleId !== $userRoleId) { - $hasNonUserRole = true; - break; - } - } - if ($hasNonUserRole && !in_array($userRoleId, $selectedRoles)) { - $selectedRoles[] = $userRoleId; - } - return $selectedRoles; - } - - /** - * Ajoute ou réactive les rôles sélectionnés pour l'utilisateur dans l'organisation. - * @param array $selectedRoles - * @param array $currentRolesMap - * @param $roleRepo - * @param User $user - * @param Organizations $organization - */ - private function addOrUpdateRoles(array $selectedRoles, array &$currentRolesMap, $roleRepo, User $user, Organizations $organization): void - { - foreach ($selectedRoles as $roleId) { - if (!isset($currentRolesMap[$roleId])) { - $roleEntity = $roleRepo->find($roleId); - if ($roleEntity) { - $newUserOrganization = new UsersOrganizations(); - $newUserOrganization->setUsers($user); - $newUserOrganization->setRole($roleEntity); - $newUserOrganization->setOrganization($organization); - $newUserOrganization->setIsActive(true); - $this->entityManager->persist($newUserOrganization); - } - } else { - $currentRolesMap[$roleId]->setIsActive(true); - $this->entityManager->persist($currentRolesMap[$roleId]); - } - unset($currentRolesMap[$roleId]); - } - } - - /** - * Désactive les rôles non sélectionnés pour l'utilisateur dans l'organisation. - * @param array $currentRolesMap - */ - private function deactivateUnselectedRoles(array $currentRolesMap): void - { - foreach ($currentRolesMap as $uo) { - $uo->setIsActive(false); - $this->entityManager->persist($uo); - } - } - - /** - * Retire les applications non sélectionnées de l'utilisateur dans l'organisation. - * @param UsersOrganizations $uoEntity - * @param array $selectedApps - */ - private function removeUnselectedApps(UsersOrganizations $uoEntity, array $selectedApps): void - { - foreach ($uoEntity->getApps()->toArray() as $existingApp) { - if (!in_array($existingApp->getId(), $selectedApps)) { - $uoEntity->removeApp($existingApp); - //Log action - $actingUser = $this->security->getUser()->getUserIdentifier(); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser]); - $this->actionService->createAction("Mise à jour des applications", - $actingUser, - $uoEntity->getOrganization(), - "{$actingUser->getIdentifier()} a retiré les droits d'accès à {$existingApp->getName()} à - {$uoEntity->getUsers()->getUserIdentifier()} pour l'organisation {$uoEntity->getOrganization()->getName()}"); - } - } - } - - /** - * Ajoute les applications sélectionnées à l'utilisateur dans l'organisation. - * @param UsersOrganizations $uoEntity - * @param array $selectedApps - */ - private function addSelectedApps(UsersOrganizations $uoEntity, array $selectedApps): void - { - foreach ($selectedApps as $appId) { - $appEntity = $this->entityManager->getRepository(Apps::class)->find($appId); - if ($appEntity && !$uoEntity->getApps()->contains($appEntity)) { - $uoEntity->addApp($appEntity); - $actingUser = $this->security->getUser()->getUserIdentifier(); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser]); - $this->actionService->createAction("Mise à jour des applications", - $actingUser, - $uoEntity->getOrganization(), - "{$actingUser->getIdentifier()} a donné les droits d'accès à {$appEntity->getName()} à - {$uoEntity->getUsers()->getUserIdentifier()} pour l'organisation {$uoEntity->getOrganization()->getName()}"); - } - } - } - - /** - * Désactive tous les rôles d'un utilisateur dans une organisation. - * - * @param User $user L'utilisateur - * @param Organizations $organization L'organisation - * @return void - */ - public function deactivateAllUserRoles(User $user, Organizations $organization): void - { - $repo = $this->entityManager->getRepository(UsersOrganizations::class); - $userOrganizations = $repo->findBy([ - 'users' => $user, - 'organization' => $organization - ]); - foreach ($userOrganizations as $uo) { - $uo->setIsActive(false); - //Log action - $actingUser = $this->security->getUser()->getUserIdentifier(); - $actingUser = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $actingUser]); - $this->actionService->createAction("Mise à jour des Roles", - $actingUser, - $uo->getOrganization(), - "{$actingUser->getIdentifier()} a désactivé le rôle {$uo->getRole()->getName()} de - {$uo->getUsers()->getUserIdentifier()} pour l'organisation {$uo->getOrganization()->getName()}"); - $this->entityManager->persist($uo); - } - $this->entityManager->flush(); - } - - /** - * Get all active users grouped by organization. - * Users with no organization are grouped under 'autre'. - * - * @return array - */ - public function getActiveUsersGroupedByOrganization(): array - { - $users = $this->entityManager->getRepository(User::class)->getAllActiveUsers(); - $userOrgs = $this->entityManager->getRepository(UsersOrganizations::class)->getAllActiveUserOrganizationLinks(); - - $userToOrgs = $this->mapUserToOrganizations($userOrgs); - - $orgs = []; - foreach ($users as $user) { - $userId = $user['id']; - if (isset($userToOrgs[$userId])) { - foreach ($userToOrgs[$userId] as $orgInfo) { - $orgId = $orgInfo['organization_id']; - if (!isset($orgs[$orgId])) { - $orgs[$orgId] = [ - 'organization_id' => $orgId, - 'organization_name' => $orgInfo['organization_name'], - 'users' => [], - ]; - } -// $orgs[$orgId]['users'][$userId] = $user; - $orgs[$orgId]['users'][$userId] = [ - 'users' => $user, - 'is_connected' => $this->userService->isUserConnected($user['email']) - ]; - } - } else { - if (!isset($orgs['autre'])) { - $orgs['autre'] = [ - 'organization_id' => null, - 'organization_name' => 'autre', - 'users' => [], - ]; - } - $orgs['autre']['users'][$userId] = [ - 'users' => $user, - 'is_connected' => $this->userService->isUserConnected($user['email']) - ]; - } - } - - // Convert users arrays to indexed arrays - foreach ($orgs as &$org) { - $org['users'] = array_values($org['users']); - } - - return array_values($orgs); - } - - /** - * Get all active users for each organization in the given array. - * - * @param Organizations[] $organizations - * @return array - */ - public function findActiveUsersByOrganizations(array $organizations): array - { - - if (empty($organizations)) { - return []; - } - - $userOrgs = $this->entityManager->getRepository(UsersOrganizations::class)->getAllActiveUserOrganizationLinks($organizations); - $usersByOrg = []; - foreach ($userOrgs as $uo) { - $org = $uo->getOrganization(); - $orgId = $org->getId(); - if (!isset($usersByOrg[$orgId])) { - $usersByOrg[$orgId] = [ - 'organization_id' => $orgId, - 'organization_name' => $org->getName(), - 'users' => [], - ]; - } - - $user = $uo->getUsers(); - $userId = $user->getId(); - - // Add connection status to user data - $usersByOrg[$orgId]['users'][$userId] = [ - 'users' => $user, - 'is_connected' => $this->userService->isUserConnected($user->getUserIdentifier()) - ]; - } - - // Convert users arrays to indexed arrays - foreach ($usersByOrg as &$org) { - $org['users'] = array_values($org['users']); - } - - return array_values($usersByOrg); - } - - /** - * Helper: Map userId to their organizations (id and name), avoiding duplicates. - * - * @param UsersOrganizations[] $userOrgs - * @return array - */ - private function mapUserToOrganizations(array $userOrgs): array - { - $userToOrgs = []; - foreach ($userOrgs as $uo) { - $userId = $uo->getUsers()->getId(); - $org = $uo->getOrganization(); - $orgId = $org->getId(); - $orgName = $org->getName(); - $userToOrgs[$userId][$orgId] = [ - 'organization_id' => $orgId, - 'organization_name' => $orgName, - ]; - } - return $userToOrgs; - } - } diff --git a/src/Service/UserService.php b/src/Service/UserService.php index 3c7822d..4bc3779 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -9,6 +9,7 @@ use App\Entity\User; use App\Entity\UsersOrganizations; use Doctrine\ORM\EntityManagerInterface; use League\Bundle\OAuth2ServerBundle\Model\AccessToken; +use Random\RandomException; class UserService { @@ -20,6 +21,7 @@ class UserService /** * Generate a random password for a new user until they set their own. + * @throws RandomException */ public function generateRandomPassword(): string{ $length = 50; // Length of the password @@ -28,38 +30,12 @@ class UserService $randomPassword = ''; for ($i = 0; $i < $length; $i++) { - $randomPassword .= $characters[rand(0, $charactersLength - 1)]; + $randomPassword .= $characters[random_int(0, $charactersLength - 1)]; } return $randomPassword; } - /** - * Check if the user is an admin in the given organization. - * - * @param int $userId - * @param int $organizationId - * @return bool - */ - public function isUserAdminInOrganization(int $userId, int $organizationId): bool - { - $user = $this->entityManager->getRepository(User::class)->find($userId); - if (!$user) { - return false; - } - $organization = $this->entityManager->getRepository(Organizations::class)->find($organizationId); - if (!$organization) { - return false; - } - $roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name'=> 'ADMIN']); - - // Check if the user is an admin in the organization - return !empty($this->entityManager->getRepository(UsersOrganizations::class)->findBy([ - 'users' => $user, - 'organization' => $organization, - 'role' => $roleAdmin])); - - } /** * Check if the user is currently connected.