403 lines
18 KiB
PHP
403 lines
18 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Entity\Actions;
|
|
use App\Entity\Apps;
|
|
use App\Entity\UsersOrganizations;
|
|
use App\Form\OrganizationForm;
|
|
use App\Repository\OrganizationsRepository;
|
|
use App\Repository\UsersOrganizationsRepository;
|
|
use App\Service\ActionService;
|
|
use App\Service\AwsService;
|
|
use App\Service\LoggerService;
|
|
use App\Service\OrganizationsService;
|
|
use App\Service\UserOrganizationService;
|
|
use App\Service\UserService;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Exception;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
use App\Entity\Organizations;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
#[Route(path: '/organization', name: 'organization_')]
|
|
class OrganizationController extends AbstractController
|
|
{
|
|
private const NOT_FOUND = 'Entity not found';
|
|
private const ACCESS_DENIED = 'Access denied';
|
|
|
|
|
|
public function __construct(private readonly EntityManagerInterface $entityManager,
|
|
private readonly UserService $userService,
|
|
private readonly OrganizationsService $organizationsService,
|
|
private readonly ActionService $actionService,
|
|
private readonly UserOrganizationService $userOrganizationService,
|
|
private readonly OrganizationsRepository $organizationsRepository,
|
|
private readonly LoggerService $loggerService, private readonly UsersOrganizationsRepository $usersOrganizationsRepository)
|
|
{
|
|
}
|
|
|
|
#[Route(path: '/', name: 'index', methods: ['GET'])]
|
|
public function index(): Response
|
|
{
|
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
$actingUser = $this->getUser();
|
|
|
|
// 1. Super Admin Case: Just show the list
|
|
if ($this->isGranted("ROLE_ADMIN")) {
|
|
return $this->render('organization/index.html.twig', ['hasOrganizations' => true]);
|
|
}
|
|
|
|
// 2. Organization Admin Case: Get their specific orgs
|
|
$orgs = $this->userOrganizationService->getAdminOrganizationsForUser($actingUser);
|
|
|
|
// If exactly one org, jump straight to it
|
|
if (count($orgs) === 1) {
|
|
return $this->redirectToRoute('organization_show', ['id' => $orgs[0]->getId()]);
|
|
}
|
|
|
|
// If multiple orgs, show the list
|
|
if (count($orgs) > 1) {
|
|
return $this->render('organization/index.html.twig', ['hasOrganizations' => true]);
|
|
}
|
|
|
|
// 3. Fallback: No access/No orgs found
|
|
$this->loggerService->logEntityNotFound('Organization', [
|
|
'user_id' => $actingUser->getUserIdentifier(),
|
|
'message' => 'No admin organizations found'
|
|
], $actingUser->getUserIdentifier());
|
|
|
|
$this->addFlash('danger', 'Erreur, aucune organisation trouvée.');
|
|
return $this->redirectToRoute('app_index');
|
|
}
|
|
|
|
#[Route(path: '/', name: 'create', methods: ['POST'])]
|
|
public function create(Request $request): JsonResponse
|
|
{
|
|
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
|
|
$actingUser = $this->getUser();
|
|
|
|
$data = $request->request->all();
|
|
|
|
// 2. Access file data specifically
|
|
$logoFile = $request->files->get('logoUrl');
|
|
|
|
$organization = new Organizations();
|
|
$existingOrg = $this->organizationsRepository->findOneBy(['email' => $data['email']]);
|
|
if ($existingOrg) {
|
|
return $this->json(['error' => 'Une organisation avec cet email existe déjà.'], 400);
|
|
}
|
|
|
|
$form = $this->createForm(OrganizationForm::class, $organization,[
|
|
'csrf_protection' => false,
|
|
'allow_extra_fields' => true,
|
|
]);
|
|
|
|
|
|
$form->submit(array_merge($data, ['logoUrl' => $logoFile]));
|
|
|
|
if ($form->isValid()) {
|
|
try {
|
|
if ($logoFile) {
|
|
$this->organizationsService->handleLogo($organization, $logoFile);
|
|
}
|
|
|
|
$organization->setProjectPrefix($this->organizationsService->generateUniqueProjectPrefix());
|
|
$organization->setName(ucFirst($organization->getName()));
|
|
$this->entityManager->persist($organization);
|
|
$this->entityManager->flush();
|
|
|
|
// Loggers...
|
|
$this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Created via ajax");
|
|
|
|
return $this->json([
|
|
'message' => 'Organisation créée avec succès.',
|
|
'id' => $organization->getId()
|
|
], Response::HTTP_CREATED);
|
|
|
|
} catch (Exception $e) {
|
|
return $this->json(['error' => $e->getMessage()], 500);
|
|
}
|
|
}
|
|
|
|
// 4. Return specific validation errors to help debugging
|
|
$errors = [];
|
|
foreach ($form->getErrors(true) as $error) {
|
|
$errors[] = $error->getMessage();
|
|
}
|
|
|
|
return $this->json(['error' => 'Validation failed', 'details' => $errors], 400);
|
|
}
|
|
|
|
#[Route(path: '/{id}', name: 'edit', methods: ['PUT'])]
|
|
public function edit(Request $request, int $id): JsonResponse
|
|
{
|
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
$actingUser = $this->getUser();
|
|
$organization = $this->organizationsRepository->find($id);
|
|
if (!$organization) {
|
|
$this->loggerService->logEntityNotFound('Organization', [
|
|
'org_id' => $id,
|
|
'message' => 'Organization not found for get endpoint'
|
|
], $actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND);
|
|
}
|
|
if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) {
|
|
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN);
|
|
}
|
|
$data = $request->request->all();
|
|
$logoFile = $request->files->get('logoUrl');
|
|
$form = $this->createForm(OrganizationForm::class, $organization,[
|
|
'csrf_protection' => false,
|
|
'allow_extra_fields' => true,
|
|
]);
|
|
if ($logoFile) {
|
|
$form->submit(array_merge($data, ['logoUrl' => $logoFile]), false);
|
|
}
|
|
$form->submit(array_merge($request->request->all(), $request->files->all()));
|
|
if ($form->isSubmitted() && $form->isValid()) {
|
|
try {
|
|
if ($logoFile) {
|
|
$this->organizationsService->handleLogo($organization, $logoFile);
|
|
}
|
|
$organization->setName(ucFirst($organization->getName()));
|
|
$this->entityManager->persist($organization);
|
|
$this->entityManager->flush();
|
|
$this->actionService->createAction("Edit Organization", $actingUser, $organization, $organization->getName());
|
|
$this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(), "Organization Edited via ajax");
|
|
return $this->json(['message' => 'Organisation modifiée avec succès.']);
|
|
} catch (Exception $e) {
|
|
return $this->json(['error' => $e->getMessage()], 500);
|
|
}
|
|
} else {
|
|
$errors = [];
|
|
foreach ($form->getErrors(true) as $error) {
|
|
$errors[] = $error->getMessage();
|
|
}
|
|
return $this->json(['error' => 'Validation failed', 'details' => $errors], 400);
|
|
}
|
|
}
|
|
|
|
|
|
#[Route(path: '/view/{id}', name: 'show', methods: ['GET'])]
|
|
public function view($id): Response
|
|
{
|
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
$organization = $this->organizationsRepository->find($id);
|
|
$actingUser = $this->getUser();
|
|
if (!$organization) {
|
|
$this->loggerService->logEntityNotFound('Organization', [
|
|
'org_id' => $id,
|
|
'message' => 'Organization not found for view'
|
|
], $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->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
|
|
|
|
$apps = $this->organizationsService->appsAccess($allApps, $orgApps);
|
|
|
|
$actions = $this->entityManager->getRepository(Actions::class)->findBy(['Organization' => $organization], orderBy: ['date' => 'DESC'], limit: 15);
|
|
$activities = $this->actionService->formatActivities($actions);
|
|
|
|
$this->actionService->createAction("View Organization", $actingUser, $organization, $organization->getName());
|
|
return $this->render('organization/show.html.twig', [
|
|
'organization' => $organization,
|
|
'applications' => $apps,
|
|
'activities' => $activities,
|
|
]);
|
|
}
|
|
|
|
#[Route(path: '/delete/{id}', name: 'delete', methods: ['POST'])]
|
|
public function delete($id): Response
|
|
{
|
|
$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->getUserIdentifier());
|
|
$this->addFlash('danger', 'Erreur, l\'organization est introuvable.');
|
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
|
}
|
|
try {
|
|
$organization->setIsActive(false);
|
|
$organization->setIsDeleted(true);
|
|
$this->organizationsService->deleteLogo($organization);
|
|
// Deactivate all associated UsersOrganizations
|
|
$this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, null, $organization);
|
|
|
|
$this->entityManager->persist($organization);
|
|
$this->actionService->createAction("Delete Organization", $actingUser, $organization, $organization->getName());
|
|
$this->entityManager->flush();
|
|
$this->loggerService->logOrganizationInformation($organization->getId(), $actingUser->getUserIdentifier(),'Organization Deleted');
|
|
if ($this->isGranted("ROLE_SUPER_ADMIN")) {
|
|
$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->getUserIdentifier(), ['message' => 'Error deleting organization: '.$e->getMessage()]);
|
|
$this->addFlash('danger', 'Erreur lors de la suppression de l\'organization.');
|
|
}
|
|
|
|
return $this->redirectToRoute('organization_index');
|
|
}
|
|
|
|
#[Route(path: '/deactivate/{id}', name: 'deactivate', methods: ['POST'])]
|
|
public function deactivate($id): Response
|
|
{
|
|
$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 deactivate'
|
|
], $actingUser->getUserIdentifier());
|
|
$this->addFlash('danger', 'Erreur, l\'organization est introuvable.');
|
|
throw $this->createNotFoundException(self::NOT_FOUND);
|
|
}
|
|
|
|
$organization->setIsActive(false);
|
|
$this->entityManager->persist($organization);
|
|
$this->actionService->createAction("Deactivate Organization", $actingUser, $organization, $organization->getName());
|
|
$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');
|
|
}
|
|
|
|
#[Route(path: '/activate/{id}', name: 'activate', methods: ['POST'])]
|
|
public function activate($id): Response
|
|
{
|
|
$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 activate'
|
|
], $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->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');
|
|
}
|
|
|
|
// API endpoint to fetch organizations data for Tabulator
|
|
#[Route(path: '/data', name: 'data', methods: ['GET'])]
|
|
public function data(Request $request): JsonResponse
|
|
{
|
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
|
|
$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
|
|
);
|
|
|
|
$total = count($paginator);
|
|
|
|
$data = array_map(function (Organizations $org) {
|
|
return [
|
|
'id' => $org->getId(),
|
|
'name' => $org->getName(),
|
|
'email' => $org->getEmail(),
|
|
'logoUrl' => $org->getLogoUrl() ?: null,
|
|
'active' => $org->isActive(),
|
|
'showUrl' => $this->generateUrl('organization_show', ['id' => $org->getId()]),
|
|
];
|
|
}, iterator_to_array($paginator));
|
|
|
|
return $this->json([
|
|
'data' => $data,
|
|
'last_page' => (int)ceil($total / $size),
|
|
'total' => $total,
|
|
]);
|
|
}
|
|
|
|
/* Ajax route to get users of an organization ( used in select field for admin of an org ) */
|
|
#[Route(path: '/{id}/users', name: 'users', methods: ['GET'])]
|
|
public function users($id): JsonResponse{
|
|
$this->denyAccessUnlessGranted("ROLE_USER");
|
|
$actingUser = $this->getUser();
|
|
$organization = $this->organizationsRepository->find($id);
|
|
if (!$organization) {
|
|
$this->loggerService->logEntityNotFound('Organization', [
|
|
'org_id' => $id,
|
|
'message' => 'Organization not found for users endpoint'
|
|
], $actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND);
|
|
}
|
|
if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) {
|
|
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN);
|
|
}
|
|
$uos = $this->usersOrganizationsRepository->findBy(['organization' => $organization, 'isActive' => true]);
|
|
$users = array_map(function (UsersOrganizations $uo) {
|
|
$user = $uo->getUsers();
|
|
return [
|
|
'id' => $user->getId(),
|
|
'email' => $user->getEmail()
|
|
];
|
|
}, $uos);
|
|
return $this->json(['users' => $users]);
|
|
}
|
|
|
|
/*
|
|
* Path used to get data on an organization for the edit modal
|
|
*/
|
|
#[Route(path: '/{id}', name: 'get', methods: ['GET'])]
|
|
public function get(int $id): JsonResponse{
|
|
$this->denyAccessUnlessGranted('ROLE_USER');
|
|
$actingUser = $this->getUser();
|
|
$organization = $this->organizationsRepository->find($id);
|
|
if (!$organization) {
|
|
$this->loggerService->logEntityNotFound('Organization', [
|
|
'org_id' => $id,
|
|
'message' => 'Organization not found for get endpoint'
|
|
], $actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::NOT_FOUND], Response::HTTP_NOT_FOUND);
|
|
}
|
|
if (!$this->userService->isAdminOfOrganization($organization) && !$this->isGranted("ROLE_ADMIN")) {
|
|
$this->loggerService->logAccessDenied($actingUser->getUserIdentifier());
|
|
return $this->json(['error' => self::ACCESS_DENIED], Response::HTTP_FORBIDDEN);
|
|
}
|
|
return $this->json([
|
|
'id' => $organization->getId(),
|
|
'name' => $organization->getName(),
|
|
'email' => $organization->getEmail(),
|
|
'logoUrl' => $organization->getLogoUrl() ?: null,
|
|
'active' => $organization->isActive(),
|
|
]);
|
|
}
|
|
}
|