<?php
namespace App\Service;
use App\Entity\Advertiser;
use App\Entity\Agency;
use App\Entity\Operation;
use App\Entity\OperationFamily;
use App\Entity\OperationOrder;
use App\Entity\OperationOrderComment;
use App\Entity\OperationOrderQuotaVersion;
use App\Entity\OperationOrderStepVersion;
use App\Entity\PolymorphicOperationOrder\OperationOrderAnimation;
use App\Entity\PolymorphicOperationOrder\OperationOrderFurniture;
use App\Entity\PolymorphicOperationOrder\OperationOrderRefrigeratorBin;
use App\Entity\Quota;
use App\Entity\Role;
use App\Entity\User;
use App\Rights\CustomSecurityLoader;
use App\Service\User as UserService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Yaml\Yaml;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
class Access
{
private Environment $twig;
private $adminListUrls;
private AdvertisingManagement $advertiserService;
private AdvertiserRightService $advertiserRightService;
private $anonListUrls;
private $authListUrls;
private AuthorizationCheckerInterface $authorizationChecker;
private EntityManagerInterface $entityManager;
private RouterInterface $routerService;
private UserService $userService;
private RequestStack $requestStack;
private ?Request $request = null;
private CustomSecurityLoader $customSecurityLoader;
private string $staciSubDomain;
public function __construct(AdvertiserRightService $advertiserRightService, UserService $userService, AdvertisingManagement $advertiser, Environment $twig, AuthorizationCheckerInterface $authorizationChecker, EntityManagerInterface $entityManager, RouterInterface $router, RequestStack $requestStack, CustomSecurityLoader $customSecurityLoader, string $staciSubDomain)
{
$this->twig = $twig;
$this->adminListUrls = [];
$this->advertiserService = $advertiser;
$this->advertiserRightService = $advertiserRightService;
$this->anonListUrls = [];
$this->authListUrls = [];
$this->authorizationChecker = $authorizationChecker;
$this->customSecurityLoader = $customSecurityLoader;
$this->entityManager = $entityManager;
$this->parseSecurityYaml();
$this->requestStack = $requestStack;
$this->request = $requestStack->getCurrentRequest();
$this->routerService = $router;
$this->userService = $userService;
$this->staciSubDomain = $staciSubDomain;
}
public function getAnonListUrls(): array
{
return $this->anonListUrls;
}
public function getAuthListUrls(): array
{
return $this->authListUrls;
}
public function getAdminListUrls(): array
{
return $this->adminListUrls;
}
public function check(string $action, object|null $entity = null): bool
{
$currentAdvertiser = $advertiser = $this->advertiserService->getCurrentAdvertiserByDomain();
$user = $this->userService->getUser();
if ($user === null) {
$scheme = $this->requestStack->getCurrentRequest() ? $this->requestStack->getCurrentRequest()->getScheme() : 'http';
$response = new RedirectResponse($scheme . '://' . $this->getDefaultUrl());
$response->send();
}
$haveToSwitchAdvertiser = false;
if ($user instanceof User && $advertiser instanceof Advertiser) {
$isGood = $this->userService->isUserAdvertiser($currentAdvertiser, $user);
if (false === $isGood) {
$userAdvertiser = $user->getAdvertiser();
if ($userAdvertiser instanceof Advertiser) {
$haveToSwitchAdvertiser = true;
}
} elseif (
$this->requestStack->getSession()->get('advertiser') === null
|| ($currentAdvertiser instanceof Advertiser && $this->requestStack->getSession()->get('advertiser') !== null && $currentAdvertiser->getId() !== $this->requestStack->getSession()->get('advertiser'))
) {
$this->writeRightInSession($user, $advertiser, null);
}
}
if (true === $haveToSwitchAdvertiser && isset($userAdvertiser) && $userAdvertiser instanceof Advertiser) {
$scheme = $this->requestStack->getCurrentRequest() ? $this->requestStack->getCurrentRequest()->getScheme() : 'http';
$requestUri = $this->requestStack->getMainRequest()->server->get('REQUEST_URI');
$requestUriCleaned = preg_replace('/\?.*/', '', (string) $requestUri);
$redirectUrl = $scheme . '://' . $userAdvertiser->getSubdomain() . $this->requestStack->getMainRequest()->server->get('COOKIE_DOMAIN') . $requestUriCleaned;
$response = new RedirectResponse($redirectUrl);
$response->send();
}
if ($entity !== null) {
if (
(method_exists($entity, 'getAdvertiser') && $entity->getAdvertiser() !== null && $entity->getAdvertiser() !== $currentAdvertiser)
|| ($entity instanceof OperationOrder && $entity->getOperation()->getAdvertiser() !== $currentAdvertiser)
|| ($entity instanceof Quota && $entity->getOperation()->getAdvertiser() !== $currentAdvertiser)
|| ($entity instanceof OperationOrder && $user->getIsAgency() === true && $this->agencyCanManageOrder($entity, $user) === false)
|| ($entity instanceof Operation && $user->getIsAgency() === true && $this->agencyCanManageOperation($entity, $user) === false)
) {
throw new HttpException(403, 'access denied');
}
}
$this->requestStack->getSession()->set('fixRaceCondition', true);
$isGranted = $this->authorizationChecker->isGranted($action, $entity);
if (false === $isGranted || $this->userService->userHaveRole($user, $advertiser) === false) {
throw new HttpException(403, 'access denied');
}
return $isGranted;
}
public function checkUrl(string $path): bool|string
{
$route = $path;
$rightValue = false;
try {
$route = $this->routerService->match($path)['_route'];
} catch (\Exception) {
}
foreach ($this->anonListUrls as $url) {
if (preg_match('!' . $url . '!', (string) $route)) {
return true;
}
}
if (true === $this->requestStack->getSession()->get('advertiserAdmin') || true === $this->userService->isAdmin(null)) {
return true;
}
foreach ($this->adminListUrls as $url) {
if (preg_match('!' . $url . '!', (string) $route)) {
return false;
}
}
if (true === $this->requestStack->getSession()->get('canAccessAdvertiser')) {
$advertiser = $this->advertiserService->getCurrentAdvertiser();
$defaultAdvertiser = $this->advertiserService->getDefaultAdvertiser();
if (!($advertiser instanceof Advertiser)) {
return false;
}
if ($route === 'index' && $advertiser !== $defaultAdvertiser) {
return true;
}
$user = $this->userService->getUser();
if (!($user instanceof User)) {
return false;
}
$rightName = null;
$userRole = $user->getRole();
if ($userRole instanceof Role) {
$rightName = 'p_' . $userRole->getId() . '_' . $route . '_view';
$rightValue = $this->requestStack->getSession()->get($rightName);
}
if (true === $rightValue || '1' === $rightValue) {
if ('p_operation_quota_view' === $rightName && str_ends_with($path, '/edit/quota')) {
if (null !== $user->getCategories()) {
$rightValue = false;
$explodedPath = explode('/', $path);
$operation = $this->entityManager->getRepository(Operation::class)->findOneBy([
'id' => $explodedPath[2],
]);
if (null !== $operation && !empty($operation->getCategories()->toArray())) {
foreach ($user->getCategories() as $category) {
if (\in_array($category, $operation->getCategories()->toArray(), true)) {
$rightValue = true;
break;
}
}
} else {
$rightValue = true;
}
}
if (null !== $user->getBusinessUnits() && !empty($user->getBusinessUnits()->toArray())) {
$rightValue = false;
$explodedPath = explode('/', $path);
$operation = $this->entityManager->getRepository(Operation::class)->findOneBy([
'id' => $explodedPath[2],
]);
if (null !== $operation && !empty($operation->getBusinessUnits()->toArray())) {
foreach ($user->getBusinessUnits()->toArray() as $buKey => $businessUnit) {
if (\in_array($businessUnit, $operation->getBusinessUnits()->toArray(), true)) {
$rightValue = true;
}
}
} else {
$rightValue = true;
}
}
}
} else {
$rightValue = false;
}
}
return $rightValue;
}
/**
* @param User|string|array|null $user
* @param object|string $entity
*/
public function checkEntityRight(mixed $user, string $action, mixed $entity): mixed
{
if (!\is_object($user)) {
$user = $this->userService->getUser();
}
if ('delete' === $action && $entity instanceof Advertiser) {
if ($this->advertiserService->isDefaultAdvertiser($entity)) {
return false;
}
}
if ('delete' === $action && $entity instanceof Role) {
if (
($entity->getIsAgency() === true && $this->userService->isAdmin($user) !== true)
|| ($entity->getIsAdmin() === true && $this->userService->isAdmin($user) !== true)
) {
return false;
}
}
$acces = $isAdmin = false;
if (\is_object($entity)) {
$entityName = basename(str_replace('\\', '/', $entity::class));
} elseif (\is_array($entity) && \array_key_exists('entityName', $entity) && \array_key_exists('id', $entity)) {
$entityName = $entity['entityName'];
$className = 'App\Entity\\' . $entityName;
/** @var class-string $className */
$entity = $this->entityManager->getRepository($className)->findOneBy(['id' => $entity['id']]);
} else {
$entityName = $entity;
}
$entityName = strtolower($entityName);
$entityName = str_replace('_', '', $entityName);
if (\in_array($entityName, ['operationorderrefrigeratorbin', 'operationorderanimation', 'operationorderfurniture'], true)) {
$entityName = 'operationorder';
}
if (\in_array($entityName, ['operationrefrigeratorbin', 'operationanimation', 'operationfurniture'], true)) {
$entityName = 'operation';
}
if (true === $this->requestStack->getSession()->get('advertiserAdmin')) {
return true;
}
$advertiser = $this->advertiserService->getCurrentAdvertiser();
if ('create' !== $action && $user instanceof User && !$this->canAccessEntityInstance($advertiser, $entity, $user)) {
return false;
}
$rightName = '';
if (true === $this->requestStack->getSession()->get('canAccessAdvertiser')) {
$rightValue = null;
$userRole = $user->getRole();
if ($userRole instanceof Role) {
$rightName = 'e_' . $userRole->getId() . '_' . $entityName . '_' . $action;
$rightValue = $this->requestStack->getSession()->get($rightName);
}
if (null !== $rightValue) {
if ('operationorder' === $entityName) {
if ($entity instanceof OperationOrder) {
$operationOrderStep = $entity->getStep();
if (\in_array($action, ['delete', 'edit'], true) && \in_array($operationOrderStep, ['abandonned', 'canceled', 'finished', 'finishedProblem'], true)) {
return false;
}
}
}
return $rightValue;
}
$advertiser = $this->advertiserService->getCurrentAdvertiser();
if (!$advertiser instanceof Advertiser) {
return false;
}
$writed = $this->writeRightInSession($user, $advertiser, $rightName);
if (true === $writed) {
$rightValue = $this->requestStack->getSession()->get($rightName);
if (null !== $rightValue) {
return $rightValue;
}
return false;
}
}
return $acces;
}
public function getDefaultUrl(): string
{
$user = $this->userService->getUser();
$path = '';
if (!$user instanceof User) {
$requestUri = $this->requestStack->getMainRequest()->server->get('REQUEST_URI');
$requestUriCleaned = preg_replace('/\?.*/', '', (string) $requestUri);
if (\in_array('user_resetpassword', $this->routerService->match($requestUriCleaned), true)) {
$path = '';
} else {
$path = '/login';
$this->setRgoto($requestUri);
}
$domain = $this->advertiserService->getSubdomain();
} else {
$userAdvertiser = $user->getAdvertiser();
if ($userAdvertiser->getStatus() === false) {
$path = '/logout';
$domain = $this->advertiserService->getSubdomain();
} else {
$domain = $userAdvertiser->getSubdomain();
}
}
if ($this->advertiserService->getSubdomain() !== $this->staciSubDomain && $domain === $this->staciSubDomain) {
return preg_replace('!' . $this->advertiserService->getSubdomain() . '.!', '', (string) $this->request->server->get('HTTP_HOST')) . $path;
}
if ($this->advertiserService->getSubdomain() === $this->staciSubDomain && $domain !== $this->staciSubDomain) {
return $domain . '.' . $this->request->server->get('HTTP_HOST') . $path;
}
return preg_replace('!' . $this->advertiserService->getSubdomain() . '!', (string) $domain, (string) $this->request->server->get('HTTP_HOST')) . $path;
}
/**
* @param User|string|null $user
* @param Advertiser|string|null $advertiser
*
* @return bool|mixed|string
*/
public function getFunctionalityRight($user, $advertiser, string $functionalityName)
{
$result = false;
if (!$user instanceof User) {
$user = $this->userService->getUser();
}
if (!$advertiser instanceof Advertiser) {
$advertiser = $this->advertiserService->getCurrentAdvertiser();
}
if (!$user instanceof User || !$advertiser instanceof Advertiser) {
return $result;
}
$userRoleID = '';
if (false === $this->userService->isAdmin($user)) {
$role = $user->getRole();
if ($role instanceof Role) {
$userRoleID = $role->getId();
}
} else {
return true;
}
$rightName = 'f_' . $userRoleID . '_' . $functionalityName . '_do';
$rightValue = $this->requestStack->getSession()->get($rightName);
if (null !== $rightValue) {
$result = $rightValue;
} else {
$writed = $this->writeRightInSession($user, $advertiser, $rightName);
if (true === $writed) {
$result = $this->requestStack->getSession()->get($rightName);
}
}
return $result;
}
public function userCanUseApiAgencyOrder(OperationOrder $operationOrder, User $user): bool
{
$result = false;
if (true === $user->getIsAgency()) {
if ($operationOrder->getAgency()->getId() === $user->getAgency()->getId()) {
$result = true;
}
}
return $result;
}
public function agencyCanManageOrder(OperationOrder $operationOrder, ?User $user): bool
{
$result = false;
if (!$user instanceof User) {
$user = $this->userService->getUser();
}
if (true === $user->getIsAgency() && $operationOrder->getAgency() === $user->getAgency()) {
$result = true;
}
return $result;
}
public function agencyCanManageOrderAction(OperationOrder $operationOrder, User $user, string $workflowName): bool
{
$result = false;
if ($this->agencyCanManageOrder($operationOrder, $user) === true) {
$agency = $user->getAgency();
if ($agency instanceof Agency) {
$placesList = $this->getAgencyPlaceListAction($user->getAdvertiser(), $agency, $workflowName);
if (\in_array($operationOrder->getStep(), $placesList, true)) {
$result = true;
}
}
}
return $result;
}
public function agencyCanManageOperation(Operation $operation, ?User $user): bool
{
$result = false;
if (!$user instanceof User) {
$user = $this->userService->getUser();
}
if (true === $user->getIsAgency() && $operation->getAgencies()->contains($user->getAgency())) {
$result = true;
}
return $result;
}
public function canModerateOrder(OperationOrder $operationOrder, ?User $user): bool
{
$result = false;
if (!$user instanceof User) {
$user = $this->userService->getUser();
}
$advertiser = $operationOrder->getOperation()->getAdvertiser();
$rightToModerate = $this->getFunctionalityRight($user, $advertiser, 'canModerateOrder');
if (
$user->getAdvertiser() === $advertiser
&& ($rightToModerate === true || $rightToModerate === 'yes')
&& \in_array($operationOrder->getStep(), ['toModerate', 'updateToModerate'], true)
) {
$result = true;
}
return $result;
}
public function render(string $path, array $parameters, int $status = 200, Advertiser $advertiser = null): Response
{
if ($advertiser === null) {
$advertiser = $this->advertiserService->getCurrentAdvertiser();
}
try {
return new Response($this->twig->render(preg_replace('!\\.!', '-' . $advertiser->getId() . '.', $path, 1), $parameters), $status);
} catch (LoaderError|RuntimeError|SyntaxError $e) {
return new Response($this->twig->render($path, $parameters), $status);
}
}
public function writeRightInSession(User $user, Advertiser $advertiser, string $rightName = null): bool
{
$writed = false;
$this->requestStack->getSession()->set('advertiserAdmin', false);
$this->requestStack->getSession()->set('canAccessAdvertiser', false);
if (null !== $rightName && !empty($rightName)) {
$this->requestStack->getSession()->set($rightName, false);
}
$this->requestStack->getSession()->set('advertiser', $advertiser->getId());
$this->requestStack->getSession()->set('user', $user->getId());
$operationFamily = $this->getUserOperationFamily($advertiser, $user);
if ($operationFamily instanceof OperationFamily) {
$this->requestStack->getSession()->set('operationFamily', $operationFamily);
}
if ($this->userService->isAdvertiserAdmin()) {
$this->requestStack->getSession()->set('advertiserAdmin', true);
$this->requestStack->getSession()->set('canAccessAdvertiser', true);
$writed = true;
} else {
$role = $user->getRole();
if ($role instanceof Role) {
$this->requestStack->getSession()->set('canAccessAdvertiser', true);
if (null === $rightName) {
$rights = $this->advertiserRightService->getRightsByRole($role);
foreach ($rights as $rightName => $rightValue) {
$this->requestStack->getSession()->set($rightName, $rightValue);
$writed = true;
}
} else {
$rightValue = $this->advertiserRightService->getRightByName($rightName);
if (!empty($rightValue)) {
$this->requestStack->getSession()->set($rightName, $rightValue);
$writed = true;
}
}
}
}
return $writed;
}
public function canMakeOrderComment(OperationOrder $operationOrder): bool
{
$result = false;
if ('finished' === $operationOrder->getStep()) {
$actualTime = new \DateTime();
if ($operationOrder instanceof OperationOrderAnimation) {
$magActionDates = $operationOrder->getMagActionDates();
if (null !== $magActionDates && !empty($magActionDates->toArray())) {
$result = true;
foreach ($magActionDates->toArray() as $key => $date) {
if ($date->getDate()->modify('+10 day') < $actualTime) {
$result = false;
}
}
}
} elseif ($operationOrder instanceof OperationOrderRefrigeratorBin || $operationOrder instanceof OperationOrderFurniture) {
$recoveryDate = $operationOrder->getRecoveryDate();
if ($recoveryDate !== null && $recoveryDate->modify('+10 day') > $actualTime) {
$result = true;
}
}
}
return $result;
}
public function getRouteFromPath(string $path): string
{
$route = '';
try {
$route = $this->routerService->match($path)['_route'];
} catch (\Exception) {
// return false;
}
return $route;
}
public function getMainRequest(): null|Request
{
return $this->requestStack->getMainRequest();
}
/**
* @param Advertiser $advertiser
* @param object|string $entity
* @param \App\Entity\User $user
*/
public function canAccessEntityInstance($advertiser, $entity, $user): bool
{
if (\is_object($entity)) {
$entityName = basename(str_replace('\\', '/', $entity::class));
} else {
$entityName = $entity;
}
$entityName = strtolower($entityName);
$entityName = str_replace('_', '', $entityName);
$speEntity = ['operationorder', 'operationorderComment', 'operationorderquotaversion', 'operationorderspeaker', 'operationoroperationorderStepversionder', 'quota', 'user'];
$acces = true;
if (\in_array($entityName, $speEntity, true)) {
if (
($entity instanceof OperationOrder && $entity->getOperation()->getAdvertiser() !== $advertiser)
|| ($entity instanceof OperationOrderComment && $entity->getOperationOrder()->getOperation()->getAdvertiser() !== $advertiser)
|| ($entity instanceof OperationOrderQuotaVersion && $entity->getOperationOrder()->getOperation()->getAdvertiser() !== $advertiser)
|| ($entity instanceof OperationOrderStepVersion && $entity->getOperationOrder()->getOperation()->getAdvertiser() !== $advertiser)
|| ($entity instanceof Quota && $entity->getOperation()->getAdvertiser() !== $advertiser)
|| ($entity instanceof User && !$this->userService->isUserAdvertiser($advertiser, $user))
) {
$acces = false;
}
} elseif (\is_object($entity) && method_exists($entity, 'getAdvertiser') && $entity->getAdvertiser() !== $advertiser) {
$acces = false;
}
return $acces;
}
public function getAgencyPlaceDatas(Advertiser $advertiser = null, Agency $agency, string $workflowName): mixed
{
if (null === $advertiser) {
$advertiser = $this->advertiserService->getCurrentAdvertiser();
}
$advertiserRef = $advertiser->getRef();
$file = sprintf('%s/config/advertisersConfig/' . $advertiserRef . '/' . $workflowName . '_agency.yaml', __DIR__ . '/../..');
if (!file_exists($file)) {
$file = sprintf('%s/config/advertisersConfig/default/' . $workflowName . '_agency.yaml', __DIR__ . '/../..');
}
return Yaml::parseFile($file);
}
public function getAgencyPlaceListShow(Advertiser $advertiser, Agency $agency, string $workflowName): array
{
$placeList = [];
$placesDatas = $this->getAgencyPlaceDatas($advertiser, $agency, $workflowName);
if (\array_key_exists('show', $placesDatas)) {
$placeList = $placesDatas['show'];
}
return $placeList;
}
public function getAgencyPlaceListEdit(Advertiser $advertiser, Agency $agency, string $workflowName): array
{
$placeList = [];
$placesDatas = $this->getAgencyPlaceDatas($advertiser, $agency, $workflowName);
if (\array_key_exists('edit', $placesDatas)) {
$placeList = $placesDatas['edit'];
}
return $placeList;
}
public function getAgencyPlaceListAction(Advertiser $advertiser, Agency $agency, string $workflowName): array
{
$placeList = [];
$placesDatas = $this->getAgencyPlaceDatas($advertiser, $agency, $workflowName);
if (\array_key_exists('action', $placesDatas)) {
$placeList = $placesDatas['action'];
}
return $placeList;
}
public function getUserOperationFamily(Advertiser $advertiser, User $user): ?OperationFamily
{
$operationFamily = $user->getOperationFamily();
if ($operationFamily === null || $operationFamily->getAdvertiser() !== $advertiser) {
$operationFamily = $this->entityManager->getRepository(OperationFamily::class)->findOneBy([
'advertiser' => $advertiser,
]);
}
return $operationFamily;
}
private function parseSecurityYaml(): void
{
$voters = $this->customSecurityLoader->load('voters_path');
$this->anonListUrls = $voters['anon'];
$this->authListUrls = $voters['auth'];
$this->adminListUrls = $voters['admin'];
}
private function setRgoto(string $url): void
{
$sfSession = $this->requestStack->getSession();
$sfSession->set('rgoto', $url);
$sfSession->save();
}
}