src/Services/Common/AclServiceV2.php line 137

Open in your IDE?
  1. <?php
  2. namespace App\Services\Common;
  3. use App\Constants\ACL;
  4. use App\Constants\UserExtension;
  5. use App\Entity\AclSetting;
  6. use App\Entity\Parameter;
  7. use App\Entity\Univers;
  8. use App\Entity\User;
  9. use App\Model\AclSettingConfig;
  10. use App\Model\Product;
  11. use App\Services\Back\ParameterService;
  12. use App\Services\Front\Catalogue\JsonCatalogueService;
  13. use Doctrine\ORM\EntityManagerInterface;
  14. use JsonException;
  15. use League\Csv\Exception;
  16. use Psr\Log\LoggerInterface;
  17. use Symfony\Component\Routing\RouterInterface;
  18. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  19. /**
  20. * Service pour la gestion des ACL V2 coté Plateforme
  21. */
  22. class AclServiceV2
  23. {
  24. private RouterInterface $router;
  25. private TokenStorageInterface $tokenStorage;
  26. private ParameterService $parameterService;
  27. private EntityManagerInterface $em;
  28. private ModuleSettingService $moduleSettingService;
  29. private JsonCatalogueService $jsonCatalogueService;
  30. private CommunityService $communityService;
  31. private LoggerInterface $logger;
  32. public function __construct(
  33. RouterInterface $router,
  34. ParameterService $parameterService,
  35. TokenStorageInterface $tokenStorage,
  36. EntityManagerInterface $em,
  37. ModuleSettingService $moduleSettingService,
  38. JsonCatalogueService $jsonCatalogueService,
  39. CommunityService $communityService,
  40. LoggerInterface $logger
  41. ) {
  42. $this->router = $router;
  43. $this->parameterService = $parameterService;
  44. $this->tokenStorage = $tokenStorage;
  45. $this->em = $em;
  46. $this->moduleSettingService = $moduleSettingService;
  47. $this->jsonCatalogueService = $jsonCatalogueService;
  48. $this->communityService = $communityService;
  49. $this->logger = $logger;
  50. }
  51. /**
  52. * @param $config
  53. *
  54. * @return false|mixed|string
  55. *
  56. * @throws JsonException
  57. */
  58. public function getNormalizedAclSettingConfigParams($config)
  59. {
  60. $config = $config instanceof AclSettingConfig ? $config->toArray() : $config;
  61. $params = $config[ 'params' ] ?? ACL::ACL_NO_PARAMS;
  62. if (is_array($params)) {
  63. if ($config[ 'route' ] !== NULL) {
  64. $params = $this->getRouteParamsForAcl($config[ 'route' ], $params);
  65. } else {
  66. $params = $this->formatArrayParamsToString($params);
  67. }
  68. } elseif (in_array($params, [NULL, ''], TRUE)) {
  69. $params = ACL::ACL_NO_PARAMS;
  70. }
  71. return $params;
  72. }
  73. /**
  74. * Retourne les params sous forme de string pour les ACL
  75. *
  76. * Supprime les params inutiles comme "_env"
  77. * Supprime les params qui ne sont pas configurés pour être pris en compte dans la route
  78. *
  79. * @param string|null $routeName
  80. * @param array|string|null $params
  81. *
  82. * @return string
  83. *
  84. */
  85. public function getRouteParamsForAcl(?string $routeName, $params): string
  86. {
  87. // cette fonction doit être capable de normalizer les params qui sont en string ou en array
  88. if (is_string($params)) {
  89. try {
  90. $params = json_decode($params, TRUE, 512, JSON_THROW_ON_ERROR);
  91. } catch (JsonException $e) {
  92. $this->logger->error($e->getMessage());
  93. $params = [];
  94. }
  95. }
  96. //unset de la clef _env présente en back_office
  97. if (isset($params[ '_env' ])) {
  98. unset($params[ '_env' ]);
  99. }
  100. $paramsOptions = $this->getAclRouteOptions($routeName);
  101. foreach ($paramsOptions as $key => $value) {
  102. if (isset($params[ $key ]) && !$value) {
  103. unset($params[ $key ]);
  104. }
  105. }
  106. return $this->formatArrayParamsToString($params);
  107. }
  108. /**
  109. * Retourne les informations de la clef acl dans les options de la route
  110. *
  111. * Certains params ne doivent pas être pris en compte pour la création des ACL (ex id d'une commande)
  112. * Il faut ajouter dans les options de la route le tableau suivant
  113. * acl :
  114. * id : false <=== le params ID ne doit pas peser dans la règle des ACL
  115. *
  116. * @param string|null $routeName
  117. *
  118. * @return array|mixed|null
  119. */
  120. public function getAclRouteOptions(?string $routeName)
  121. {
  122. if ($routeName === NULL) {
  123. return [];
  124. }
  125. // Récupère les informations complètes de la route courante
  126. $route = $this->router->getRouteCollection()->get($routeName);
  127. if ($route === NULL) {
  128. return [];
  129. }
  130. return $route->getOption('acl') ?? [];
  131. }
  132. /**
  133. * Normalise le json_encode des params pour la transformation array to string
  134. *
  135. * @param array|null $params
  136. *
  137. * @return string
  138. */
  139. public function formatArrayParamsToString(?array $params): string
  140. {
  141. if ($params === NULL) {
  142. return ACL::ACL_NO_PARAMS;
  143. }
  144. try {
  145. return json_encode($params, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
  146. } catch (JsonException $e) {
  147. $this->logger->error($e->getMessage());
  148. return ACL::ACL_NO_PARAMS;
  149. }
  150. }
  151. /**
  152. * Récupère l'environnement d'une route (front ou back) par le début du routename
  153. *
  154. * @param string $routeName
  155. *
  156. * @return string
  157. */
  158. public function getEnvByRoute(string $routeName): string
  159. {
  160. if (str_contains($routeName, ACL::START_ROUTE_BACK)) {
  161. return ACL::BACK_ENV;
  162. }
  163. return ACL::FRONT_ENV;
  164. }
  165. /**
  166. * Remplit le fichier d'ACL avec les données lors du POST de la modale
  167. *
  168. * @param AclSettingConfig $config
  169. * @param array $acl
  170. * @param bool $debug
  171. *
  172. * @return array
  173. */
  174. public function setAllData(AclSettingConfig $config, array $acl = [], bool $debug = FALSE): array
  175. {
  176. $acls = [];
  177. foreach ($acl as $role => $jobs) {
  178. $config->setRole($role);
  179. foreach ($jobs as $job => $value) {
  180. $config->setJob($job);
  181. $acls[] = $this->setData($config, $value, FALSE);
  182. }
  183. }
  184. // reset de la config si on en a besoin ultérieurement
  185. $config
  186. ->setRole(NULL)
  187. ->setJob(NULL)
  188. ;
  189. $this->em->flush();
  190. return $acls;
  191. }
  192. /**
  193. * Set 1 ligne d'acl uniquement
  194. *
  195. * @param AclSettingConfig $config
  196. * @param bool $value
  197. * @param bool $withFlush
  198. * @param bool $debug
  199. *
  200. * @return AclSetting|float|int|mixed|string|null
  201. */
  202. public function setData(AclSettingConfig $config, bool $value, bool $withFlush = TRUE, bool $debug = FALSE)
  203. {
  204. // SI pas de role set, on met par défaut le ROLE_USER
  205. // TODO voir si on retourne une erreur à la place
  206. if ($config->getRole() === NULL) {
  207. $config->setRole('ROLE_USER');
  208. }
  209. // Si pas de job set, on met la constant ACL_NO_JOB
  210. if ($config->getJob() === NULL) {
  211. $config->setRole(ACL::ACL_NO_JOB);
  212. }
  213. // vérifications des params en fonction de la configuration des routes
  214. $params = $this->getRouteParamsForAcl($config->getRoute(), $config->getParams());
  215. $config->setParams($params);
  216. $registeredAcl = $this->em->getRepository(AclSetting::class)->getAclSettingsFor($config, TRUE);
  217. if ($registeredAcl === NULL) {
  218. $aclSetting = (new AclSetting())
  219. ->setFromAclSettingConfig($config)
  220. ->setConcatKey($config->getConcatKey())
  221. ;
  222. $this->em->persist($aclSetting);
  223. } else {
  224. $aclSetting = $registeredAcl;
  225. }
  226. $aclSetting
  227. ->setValue($value)
  228. ;
  229. if ($withFlush) {
  230. $this->em->flush();
  231. }
  232. return $aclSetting;
  233. }
  234. /**
  235. * @param $config
  236. *
  237. * @return string
  238. * @deprecated
  239. */
  240. private function getConcatKey($config): string
  241. {
  242. $default = [
  243. 'env' => ACL::FRONT_ENV,
  244. 'route' => NULL,
  245. 'params' => ACL::ACL_NO_PARAMS,
  246. 'component' => ACL::ACL_NO_COMPONENT,
  247. 'slug' => ACL::ACL_NO_SLUG,
  248. 'action' => ACL::READ,
  249. ];
  250. $config = array_merge($default, $config);
  251. return $config[ 'env' ] . ACL::ACL_KEY_SEPARATOR . ($config[ 'route' ] ?? '') . ACL::ACL_KEY_SEPARATOR . $config[ 'params' ] . ACL::ACL_KEY_SEPARATOR . $config[ 'component' ] . ACL::ACL_KEY_SEPARATOR . $config[ 'slug' ] . ACL::ACL_KEY_SEPARATOR . $config[ 'action' ];
  252. }
  253. /**
  254. * Retourne l'objet utilisé pour construire le tableau d'ACL dans la modale
  255. *
  256. * @param AclSettingConfig $config
  257. *
  258. * @return array
  259. */
  260. public function getAclConfig(AclSettingConfig $config): array
  261. {
  262. // on garde la route d'origine en mémoire
  263. $routeName = $config->getRoute();
  264. if (NULL === $routeName) {
  265. return [];
  266. }
  267. // on transforme les valeurs pour correspondre aux différents cas
  268. $config = $this->transformAclVariables($config);
  269. $aclItems = $this->getAclItems($config);
  270. $tables = [];
  271. foreach ($aclItems as $role => $jobs) {
  272. $rows = [];
  273. $rows[] = array_merge([$role], array_values($jobs));
  274. $tables[ $role ] = [
  275. 'header' => [array_keys($jobs)],
  276. 'row' => $rows,
  277. ];
  278. // Ajoute au début du tableau $jobs le $role
  279. $rows = [array_merge([$role], array_values($jobs))];
  280. $tables[ $role ] = [
  281. 'rows' => $rows,
  282. 'header' => array_keys($jobs),
  283. ];
  284. }
  285. $data = [
  286. 'method' => 'NaN',
  287. 'env' => $config->getEnv(),
  288. 'route' => $config->getRoute(),
  289. 'params' => $config->getParams(),
  290. 'component' => $config->getComponent(),
  291. 'slug' => $config->getSlug(),
  292. 'action' => $config->getAction(),
  293. 'tables' => $tables,
  294. ];
  295. $route = $this->router->getRouteCollection()->get($routeName);
  296. if ($route !== NULL) {
  297. $defaults = $route->getDefaults();
  298. // On remplace ':' par '::' pour pouvoir utiliser la reflection
  299. $re = '/(.*\w):(\w.*)/m';
  300. $subst = "$1::$2";
  301. $defaults[ '_controller' ] = preg_replace($re, $subst, $defaults[ '_controller' ]);
  302. $method = explode('::', $defaults[ '_controller' ]);
  303. if (method_exists($method[ 0 ], $method[ 1 ])) {
  304. $data[ 'method' ] = $defaults[ '_controller' ];
  305. }
  306. }
  307. $data[ 'routeName' ] = $routeName;
  308. return $data;
  309. }
  310. /**
  311. * Prépare les variables d'ACL en fonction de cas particulier
  312. *
  313. * - slug pas toujours obligatoire
  314. * - les composants "commun" (header, footer) ne dépendent pas de la route
  315. * - les catalogues ne dépendent pas des routes
  316. *
  317. * @param AclSettingConfig $config
  318. *
  319. * @return AclSettingConfig
  320. */
  321. private function transformAclVariables(AclSettingConfig $config): AclSettingConfig
  322. {
  323. // le slug n'est pas toujours obligatoire
  324. if (in_array($config->getSlug(), ['', NULL], TRUE)) {
  325. $config->setSlug(ACL::ACL_NO_SLUG);
  326. }
  327. // les components dans la partie common ne sont pas dépendant de la page
  328. if (strpos($config->getComponent(), 'common.') === 0) {
  329. $config->setRoute(ACL::ACL_ROUTE_FRONT_ALL);
  330. $config->setParams(ACL::ACL_NO_PARAMS);
  331. }
  332. // même principe pour le header dans le back office
  333. if ($config->getEnv() === ACL::BACK_ENV && strpos($config->getComponent(), 'header.') === 0) {
  334. $config->setRoute(ACL::ACL_ROUTE_BACK_ALL);
  335. $config->setParams(ACL::ACL_NO_PARAMS);
  336. }
  337. // on s'occupe d'un acl global de catalogue
  338. if (strpos($config->getSlug(), ACL::ACL_KEY_SLUG_SHOP_CATALOG) === 0) {
  339. $config->setRoute(ACL::ACL_ROUTE_SHOP_CONFIG);
  340. $config->setParams(ACL::ACL_NO_PARAMS);
  341. $config->setEnv(ACL::FRONT_ENV);
  342. }
  343. return $config;
  344. }
  345. /**
  346. * Donne l'ACL correspondant à l'environnement, la route, au composant et à l'action demandés et retourne un
  347. * tableau formaté pour l'affichage de la modale d'édition
  348. *
  349. * @param AclSettingConfig $config
  350. *
  351. * @return array
  352. */
  353. private function getAclItems(AclSettingConfig $config): array
  354. {
  355. $currentUser = $this->tokenStorage->getToken() !== NULL ? $this->tokenStorage->getToken()->getUser() : NULL;
  356. // on est obligé de transformer les clefs en premier en fonction des conditions
  357. $config = $this->transformAclVariables($config);
  358. $rolesAndJobs = $this->getDefaultRoleAndJob();
  359. $acls = [];
  360. foreach ($rolesAndJobs as $role => $jobs) {
  361. $config->setRole($role);
  362. foreach ($jobs as $job => $value) {
  363. $config->setJob($job);
  364. $acls[] = $this->getAclItemForRoleAndJob($config, TRUE, FALSE);
  365. }
  366. }
  367. $formattedResult = [];
  368. // Pour chaque acl, injecte dans $formattedResult les roles et jobs voulus
  369. /** @var AclSetting $acl */
  370. foreach ($acls as $acl) {
  371. $formattedResult[ $acl->getRole() ][ $acl->getJob() ] = $acl->getValue();
  372. }
  373. // Si on est ROLE_ADMIN, on a accès au tableau pour les ROLE_USER
  374. if ($currentUser instanceof User && $currentUser->isAdmin()) {
  375. unset($formattedResult[ 'ROLE_SUPER_ADMIN' ], $formattedResult[ 'ROLE_DEVELOPER' ]);
  376. }
  377. // Si on est ROLE_SUPER_ADMIN, on a accès au tableau pour les ROLE_USER, et ROLE_ADMIN
  378. if ($currentUser instanceof User && $currentUser->isSuperAdmin()) {
  379. unset($formattedResult[ 'ROLE_DEVELOPER' ]);
  380. }
  381. return $formattedResult;
  382. }
  383. /**
  384. * Retourne le tableau des roles et job en fonction de la configuration YAML
  385. *
  386. * Si un role n'a pas de job, le système des acls le considère avec le job ACL::ACL_NO_JOB
  387. *
  388. * @return array[]
  389. */
  390. public function getDefaultRoleAndJob(bool $debug = FALSE): array
  391. {
  392. $tree = $this->communityService->getTreeJobs();
  393. $roles = [
  394. 'ROLE_USER' => array_merge(array_keys(array_filter($tree, static function ($item) {
  395. return !isset($item[ 'role' ]) || $item[ 'role' ] === 'ROLE_USER';
  396. })), [ACL::ACL_NO_JOB]),
  397. 'ROLE_ADMIN' => array_merge(array_keys(array_filter($tree, static function ($item) {
  398. return isset($item[ 'role' ]) && $item[ 'role' ] === 'ROLE_ADMIN';
  399. })), [ACL::ACL_NO_JOB]),
  400. 'ROLE_SUPER_ADMIN' => array_merge(array_keys(array_filter($tree, static function ($item) {
  401. return isset($item[ 'role' ]) && $item[ 'role' ] === 'ROLE_SUPER_ADMIN';
  402. })), [ACL::ACL_NO_JOB]),
  403. ];
  404. if (isset($roles[ 'ROLE_DEVELOPER' ])) {
  405. unset($roles[ 'ROLE_DEVELOPER' ]);
  406. }
  407. return array_map(static function ($role) {
  408. // Si $role est vide, ajoute une clé 'ACL::ACL_NO_JOB'
  409. if (count($role) === 0) {
  410. return [ACL::ACL_NO_JOB => TRUE];
  411. }
  412. return array_map(static function () {
  413. return TRUE;
  414. }, array_flip($role));
  415. }, $roles);
  416. }
  417. /**
  418. * Retourne une règle d'ACL complète
  419. *
  420. * Retourne une règle existante ou en créé une nouvelle par rapport au contexte
  421. * Force la valeur à FALSE pour la création d'une règle qui concerne le back + un ROLE_USER
  422. *
  423. * @param AclSettingConfig $config config complète contexte + ROLE + JOB
  424. * @param bool $withFlush ajoute un flush dans la fonction pour enregistrer AclSetting
  425. * @param bool $debug
  426. *
  427. * @return AclSetting|float|int|mixed|string|null
  428. */
  429. public function getAclItemForRoleAndJob(AclSettingConfig $config, bool $withFlush = TRUE, bool $debug = FALSE)
  430. {
  431. $acl = $this->em->getRepository(AclSetting::class)->getAclSettingsFor($config, TRUE, TRUE);
  432. // si les clefs n'existent pas, on les set pour chaque role/job
  433. if ($acl === NULL)
  434. {
  435. // Si la règle concerne le back pour un ROLE_USER, on set à FALSE par défaut
  436. if ($config->getEnv() === ACL::BACK_ENV && $config->getRole() === "ROLE_USER") {
  437. $defaultValue = FALSE;
  438. }
  439. else
  440. {
  441. $defaultValues = $this->getDefaultRoleAndJob();
  442. try {
  443. $defaultValue = (bool)$defaultValues[ $config->getRole() ][ $config->getJob() ];
  444. } catch (\Exception $e) {
  445. $this->logger->error($e->getMessage());
  446. $defaultValue = FALSE;
  447. }
  448. }
  449. $acl = $this->setData(
  450. $config,
  451. $defaultValue,
  452. $withFlush,
  453. $debug,
  454. );
  455. }
  456. return $acl;
  457. }
  458. /**
  459. * Vérifie si le user à le droit de voir le produit
  460. *
  461. * Vérifie les catalogues où est présent le produit et recherche les droits d'accès du user sur ces catalogues
  462. * Retourne TRUE au premier qui match
  463. *
  464. * @param User|null $user
  465. * @param Product $product
  466. *
  467. * @return bool
  468. *
  469. * @throws JsonException
  470. */
  471. public function userIsGrantedProduct(?User $user, Product $product): bool
  472. {
  473. foreach ($product->getCatalogues() as $catalogue) {
  474. //if ($this->userIsGrantedCatalogue($user, $catalogue) && $this->jsonCatalogueService->isProductInCatalogue($product->getSku(), $catalogue)) {
  475. //TODO @Manu la vérification pour savoir si le produit est dans le catalogue est lourde, sans doute redondante si on a déjà récupéré le produit via le JSON pour obtenir la variable $product
  476. if ($this->userIsGrantedCatalogue($user, $catalogue)) {
  477. return TRUE;
  478. }
  479. }
  480. return FALSE;
  481. }
  482. /**
  483. * Retourne si l'utilisateur peut voir ou non le catalogue via son slug, prend en compte les Univers si
  484. * l'option est active
  485. *
  486. * @param User|null $user
  487. * @param string $catalogueSlug
  488. * @param bool $debug
  489. *
  490. * @return bool
  491. *
  492. * @throws JsonException
  493. */
  494. public function userIsGrantedCatalogue(?User $user, string $catalogueSlug, bool $debug = FALSE): bool
  495. {
  496. $isGranted = $this->userIsGranted(
  497. $user,
  498. [
  499. 'route' => ACL::ACL_ROUTE_SHOP_CONFIG,
  500. 'params' => ACL::ACL_NO_PARAMS,
  501. 'component' => ACL::ACL_NO_COMPONENT,
  502. 'slug' => ACL::ACL_KEY_SLUG_SHOP_CATALOG . '.' . $catalogueSlug,
  503. 'env' => ACL::FRONT_ENV,
  504. ],
  505. );
  506. // en cas d'univers il faut vérifier si on a le droit de voir quand on est ni dev, ni super admin
  507. $universActive = $this->moduleSettingService->isModuleActive('univers');
  508. if ($universActive && $user !== NULL && !$user->isSuperAdmin() && !$user->isDeveloper()) {
  509. $catalogueHasUnivers = $this->em->getRepository(Univers::class)->findUniversForUserAndCatalogSlug($user, $catalogueSlug);
  510. $isGranted = $catalogueHasUnivers !== [];
  511. }
  512. return $isGranted;
  513. }
  514. /**
  515. * Indique si un utilisateur a les droits d'accès de la page ou du composant avec son action
  516. *
  517. * Retourne TRUE si on est sur un component qui s'affiche coté security (non logué)
  518. * Retourne TRUE si on est ROLE_DEVELOPER
  519. * Retourne FALSE si la règle n'est pas trouvée ou qu'aucune règle n'est TRUE
  520. * Retourne TRUE à la première règle dont la valeur est TRUE (si le user à plusieurs roles par exemple)
  521. *
  522. * @param User|null $user
  523. * @param array $config
  524. * @param bool $debug
  525. *
  526. * @return bool
  527. * @throws JsonException
  528. */
  529. public function userIsGranted(
  530. ?User $user,
  531. array $config,
  532. bool $debug = FALSE
  533. ): bool
  534. {
  535. $default = [
  536. 'route' => NULL,
  537. 'params' => ACL::ACL_NO_PARAMS,
  538. 'component' => ACL::ACL_NO_COMPONENT,
  539. 'slug' => ACL::ACL_NO_SLUG,
  540. 'action' => ACL::READ,
  541. 'env' => ACL::FRONT_ENV,
  542. ];
  543. $config = array_merge($default, $config);
  544. // cette étape normalise la variable params que ça soit une string ou un array.
  545. $config[ 'params' ] = $this->getRouteParamsForAcl($config[ 'route' ], $config[ 'params' ]);
  546. //Si c'est un component qui vient de la clef security alors on autorise
  547. // @todo passer par l'array des routes concernée par la security ?
  548. // security_path dans twig.yaml
  549. if (strpos($config[ 'component' ], 'security.', 0) !== FALSE || in_array($config[ 'route' ], ACL::ACL_SECURITY_ROUTES, TRUE)) {
  550. return TRUE;
  551. }
  552. // Utilisateur non connecté
  553. if (!$user instanceof User) return FALSE;
  554. // Aucune restriction pour le ROLE_DEVELOPER
  555. if ($user->isDeveloper()) return TRUE;
  556. $isGranted = FALSE;
  557. foreach ($user->getRoles() as $role)
  558. {
  559. $aclConfig =
  560. (new AclSettingConfig())
  561. ->setFromArray($config)
  562. ->setRole($role)
  563. ->setJob($user->getJob() ?? ACL::ACL_NO_JOB)
  564. ;
  565. $aclConfig = $this->transformAclVariables($aclConfig);
  566. $aclItem = $this->getAclItemForRoleAndJob($aclConfig, TRUE, $debug);
  567. $isGranted = $aclItem->getValue();
  568. if ($isGranted) break;
  569. }
  570. return $isGranted;
  571. }
  572. /**
  573. * Retourne le slug du premier catalogue où le user a les accès ACL
  574. *
  575. * @param User|null $user
  576. * @param Product $product
  577. *
  578. * @return mixed|null
  579. * @throws JsonException
  580. */
  581. public function getUserFirstGrantedCatalogSlugForProduct(?User $user, Product $product)
  582. {
  583. foreach ($product->getCatalogues() as $catalogue) {
  584. //TODO @manu la vérification pour savoir si le produit est dans le catalogue est lourde, sans doute redondante si on a déjà récupéré le produit via le JSON pour obtenir la variable $product
  585. if ($this->userIsGrantedCatalogue($user, $catalogue) /*&& $this->jsonCatalogueService->isProductInCatalogue($product->getSku(), $catalogue)*/) {
  586. return $catalogue;
  587. }
  588. }
  589. return NULL;
  590. }
  591. /**
  592. * Indique si un user a les droits d'accès au document
  593. *
  594. * Les ACL du document se font lors de la création de l'entité
  595. * Choix des roles => si pas de choix tout le monde voit
  596. * Choix des jobs => si pas de choix tout le monde voit
  597. * Choix des univers => si pas de choix tout le monde voit
  598. *
  599. * @param User|null $user
  600. * @param Parameter $document
  601. *
  602. * @return bool
  603. *
  604. * @throws JsonException
  605. * @throws Exception
  606. */
  607. public function userIsGrantedOnDocument(?User $user, Parameter $document): bool
  608. {
  609. if ($user === NULL) {
  610. return TRUE;
  611. }
  612. $isGranted = TRUE;
  613. //Check sur job
  614. if ($document->getDisplayJob() !== NULL
  615. && !in_array($user->getJob(), $document->getDisplayJob(), TRUE)
  616. ) {
  617. return FALSE;
  618. }
  619. //Check sur le role
  620. if ($document->getDisplayRole() !== NULL && array_diff(
  621. $user->getRoles(),
  622. $document->getDisplayRole(),
  623. ) !== []) {
  624. return FALSE;
  625. }
  626. $universes = $document->getDisplayUniverses();
  627. // Si config par univers on regarde si ça match
  628. if ($universes !== NULL) {
  629. $isGranted = FALSE;
  630. foreach ($user->getUniverses() as $userUnivers) {
  631. if (in_array($userUnivers->getSlug(), $universes, TRUE)) {
  632. $isGranted = TRUE;
  633. break;
  634. }
  635. }
  636. if (!$isGranted) {
  637. return FALSE;
  638. }
  639. }
  640. // Check si la selection des documents est activé sur des parents dont il dépend et qui ont une extension
  641. if ($user->getParents()->getValues() !== []) {
  642. $isGranted = $this->parameterService->isDocumentSelectedByUserParents($user, $document->getId());
  643. }
  644. // sur le user lui-même si une extension existe
  645. $currentUserDocumentSelected = $user->getExtensionBySlug(UserExtension::DOCUMENT_SELECTION);
  646. if ($currentUserDocumentSelected !== NULL) {
  647. $isGranted = $this->parameterService->isDocumentSelectedForUser($user, $document->getId());
  648. }
  649. return $isGranted;
  650. }
  651. /**
  652. * Définit si un element d'un formType est visible en fonction de ses droits configurés dans le yaml
  653. *
  654. * @param AclSettingConfig|null $aclConfig
  655. * @param array $config
  656. * @param bool $debug
  657. *
  658. * @return bool
  659. * @throws JsonException
  660. */
  661. public function currentUserIsGrantedByConfigFormType(?AclSettingConfig $aclConfig, array $config = [], bool $debug = FALSE): bool
  662. {
  663. // Pas de config, tout le monde voit
  664. if ($aclConfig === NULL &&
  665. !array_key_exists('jobs', $config) &&
  666. !array_key_exists('roles', $config) &&
  667. !array_key_exists('univers', $config)) {
  668. return TRUE;
  669. }
  670. $tokenStorage = $this->tokenStorage->getToken();
  671. $currentUser = $tokenStorage ? $tokenStorage->getUser() : NULL;
  672. if (!$currentUser instanceof User) {
  673. return FALSE;
  674. }
  675. // Le super_admin et dev doivent pouvoir tout voir
  676. if ($currentUser->isDeveloper() || $currentUser->isSuperAdmin()) {
  677. return TRUE;
  678. }
  679. if (array_key_exists('jobs', $config) && $config[ 'jobs' ] !== null) {
  680. return $this->canDisplayByJobs($currentUser, $config[ 'jobs' ]);
  681. }
  682. if (array_key_exists('roles', $config) && $config[ 'roles' ] !== null) {
  683. return $this->canDisplayByRoles($currentUser, $config[ 'roles' ]);
  684. }
  685. $universesConfig = isset($config[ 'univers' ]) && count($config[ 'univers' ]) > 0;
  686. // cas 2 $aclConfig + config => on ne garde que les univers pour le moment, $aclConfig prend le dessus.
  687. $isGranted = !$aclConfig || $this->userIsGranted($currentUser, $aclConfig->toArray(), $debug);
  688. if ($isGranted && $universesConfig) {
  689. return $this->canDisplayByUniverses($currentUser, $config[ 'univers' ]);
  690. }
  691. return $isGranted;
  692. }
  693. /**
  694. * Vérifie si on peut afficher un élément en fonction du roles
  695. *
  696. * @param User $user
  697. * @param array $roles
  698. *
  699. * @return bool
  700. */
  701. public function canDisplayByRoles(User $user, array $roles): bool
  702. {
  703. $userRole = $user->getRoles();
  704. if(array_diff($userRole, $roles) === []) {
  705. return TRUE;
  706. }
  707. return FALSE;
  708. }
  709. /**
  710. * Vérifie si on peut afficher un élément en fonction du jobs
  711. *
  712. * @param User $user
  713. * @param array $jobs
  714. *
  715. * @return bool
  716. */
  717. public function canDisplayByJobs(User $user, array $jobs): bool
  718. {
  719. $userJob = $user->getJob();
  720. if(in_array($userJob, $jobs)) {
  721. return TRUE;
  722. }
  723. return FALSE;
  724. }
  725. /**
  726. * Vérifie si on peut afficher un élément en fonction des univers de l'utilisateur
  727. *
  728. * @param User $user
  729. * @param array $universes
  730. *
  731. * @return bool
  732. */
  733. public function canDisplayByUniverses(User $user, array $universes): bool
  734. {
  735. $userUniverses = $user->getUniverses();
  736. foreach ($userUniverses as $univers) {
  737. if (in_array($univers->getSlug(), $universes, TRUE)) {
  738. return TRUE;
  739. }
  740. }
  741. return FALSE;
  742. }
  743. }