src/Twig/Runtime/AclRuntime.php line 223

Open in your IDE?
  1. <?php
  2. /** @noinspection ALL */
  3. namespace App\Twig\Runtime;
  4. use App\Constants\ACL;
  5. use App\Entity\Parameter;
  6. use App\Entity\SliderItem;
  7. use App\Entity\User;
  8. use App\Model\Product;
  9. use App\Services\Back\ParameterService;
  10. use App\Services\Common\AclServiceV2;
  11. use Doctrine\ORM\EntityManagerInterface;
  12. use Psr\Cache\InvalidArgumentException;
  13. use Symfony\Component\HttpFoundation\RequestStack;
  14. use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken;
  15. use Symfony\Component\Security\Core\Security;
  16. use Symfony\Component\Security\Core\User\UserInterface;
  17. use Twig\Extension\RuntimeExtensionInterface;
  18. use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
  19. /**
  20. * Rassemble toute les fonction disponible dans twig qui touchent aux ACL et à l'affichage des données via les règles acl ou les systèmes paralèlles (roles, jobs sur les entités comme pour les slider ou les documents)
  21. */
  22. class AclRuntime implements RuntimeExtensionInterface
  23. {
  24. private AclServiceV2 $aclService;
  25. private RequestStack $requestStack;
  26. private ParameterService $parameterService;
  27. private Security $security;
  28. private RoleHierarchyInterface $roleHierarchy;
  29. private EntityManagerInterface $em;
  30. public function __construct(
  31. AclServiceV2 $aclService,
  32. RequestStack $requestStack,
  33. ParameterService $parameterService,
  34. Security $security,
  35. RoleHierarchyInterface $roleHierarchy,
  36. EntityManagerInterface $em
  37. )
  38. {
  39. $this->aclService = $aclService;
  40. $this->requestStack = $requestStack;
  41. $this->parameterService = $parameterService;
  42. $this->security = $security;
  43. $this->roleHierarchy = $roleHierarchy;
  44. $this->em = $em;
  45. }
  46. /**
  47. * Permet de savoir si le user a le droit d'accéder à la route
  48. * @param User|null $user
  49. * @param string $route
  50. * @param array $params
  51. * @param string $env
  52. * @return bool
  53. */
  54. public function userIsGrantedRoute(
  55. ?User $user,
  56. string $route,
  57. array $params = [],
  58. string $env = ACL::FRONT_ENV,
  59. bool $debug = FALSE
  60. )
  61. {
  62. $config = [
  63. 'route' => $route,
  64. 'params' => $params,
  65. 'component' => ACL::ACL_NO_COMPONENT,
  66. 'slug' => ACL::ACL_NO_SLUG,
  67. 'action' => ACL::READ,
  68. 'env' => $env,
  69. ];
  70. return $this->aclService->userIsGranted( $user, $config, $debug );
  71. }
  72. /**
  73. * Permet de savoir si un user peut faire une action en fonction de son rôle ou de son job
  74. *
  75. * @param User|null $user
  76. * @param string $slug
  77. * @param string $action
  78. * @param string $env
  79. *
  80. * @return bool
  81. *
  82. * @throws InvalidArgumentException
  83. */
  84. public function userIsGranted(
  85. ?User $user,
  86. string $component,
  87. string $slug = ACL::ACL_NO_SLUG,
  88. string $action = ACL::READ,
  89. string $env = ACL::FRONT_ENV,
  90. string $route = NULL,
  91. string $params = NULL,
  92. bool $debug = FALSE
  93. ): bool
  94. {
  95. $currentRoute = $route ?? $this->requestStack->getCurrentRequest()->get( '_route' );
  96. $currentParams = $params ?? $this->requestStack->getCurrentRequest()->get( '_route_params' );
  97. if ($currentRoute === NULL){
  98. return $this->checkWhenRouteIsNull();
  99. }
  100. $config = [
  101. 'route' => $currentRoute,
  102. 'params' => $this->aclService->getRouteParamsForAcl($currentRoute, $currentParams),
  103. 'component' => $component,
  104. 'slug' => $slug,
  105. 'action' => $action,
  106. 'env' => $env,
  107. ];
  108. return $this->aclService->userIsGranted( $user, $config, $debug );
  109. }
  110. /**
  111. * Logique pour éviter des erreurs 500 si jamais une route est évaluée à NULL
  112. * Si on est dans le cas d'une exception on autorise la visualition
  113. * @return true
  114. * @throws \Exception
  115. */
  116. private function checkWhenRouteIsNull()
  117. {
  118. $request = $this->requestStack->getCurrentRequest();
  119. $exeption = $request->attributes->get('exception');
  120. if ($exeption !== null) {
  121. return TRUE;
  122. }
  123. throw new \Exception('Une erreur est survenue, la route ne peut pas être null et ne pas être une exception');
  124. }
  125. /**
  126. * Permet de savoir si le current user à le droit de voir le catalogue par son slug
  127. * @param $catalogue
  128. * @return bool
  129. * @throws InvalidArgumentException
  130. */
  131. public function userIsGrantedCatalogue($catalogue)
  132. {
  133. $currentUser = $this->security->getUser();
  134. return $this->aclService->userIsGrantedCatalogue($currentUser, $catalogue);
  135. }
  136. /**
  137. * Permet de savoir si le current user à le droit de voir le produit
  138. * @param Product $product
  139. *
  140. * @return bool
  141. * @throws \JsonException
  142. */
  143. public function userIsGrantedProduct(Product $product)
  144. {
  145. $currentUser = $this->security->getUser();
  146. return $this->aclService->userIsGrantedProduct($currentUser, $product);
  147. }
  148. /**
  149. * Retourne le slug du premier catalogue qui contient le produit et qui est accèssible au user courant
  150. * @param Product $product
  151. *
  152. * @return mixed|null
  153. * @throws \JsonException
  154. */
  155. public function getUserFirstGrantedCatalogSlugForProduct(Product $product)
  156. {
  157. $currentUser = $this->security->getUser();
  158. return $this->aclService->getUserFirstGrantedCatalogSlugForProduct($currentUser, $product);
  159. }
  160. /**
  161. * Défini si l'utilisateur à le droit de voir le document
  162. *
  163. * @param User|null $user
  164. * @param Parameter $document
  165. *
  166. * @return bool
  167. *
  168. * @throws \JsonException
  169. */
  170. public function canDisplayDocument( ?User $user, Parameter $document ): bool
  171. {
  172. return $this->aclService->userIsGrantedOnDocument( $user, $document );
  173. }
  174. /**
  175. * Lors des documents personnalisé, permet de savoit si le user peut voir le document
  176. *
  177. * @param User|null $user
  178. * @param $id
  179. *
  180. * @return bool
  181. *
  182. * @throws \JsonException
  183. */
  184. public function isDocumentSelectedForUser( ?User $user, $id ): bool
  185. {
  186. return $this->parameterService->isDocumentSelectedForUser( $user, $id );
  187. }
  188. /**
  189. * Permet de savoir si on component est visible par le user courant
  190. *
  191. * Ne doit être utilisé que pour l'affichage twig des components en front
  192. *
  193. * @param array|null $componentOptions
  194. * @param bool $debug
  195. * @return bool
  196. * @throws InvalidArgumentException
  197. */
  198. public function canDisplayComponentByAcl(?array $componentOptions, bool $debug = FALSE)
  199. {
  200. if ($componentOptions === NULL || $componentOptions === []){
  201. return TRUE;
  202. }
  203. $item = $componentOptions['item'] ?? $componentOptions;
  204. // Le component est actif sur la page ?
  205. $canDisplay = (bool)$item[ 'enabled' ] ?? TRUE;
  206. if (!$canDisplay) {
  207. return FALSE;
  208. }
  209. // on regarde s'il peut s'afficher sur la page via la clef display
  210. $canDisplay = $this->canDisplayOnPageByConfig($item['display'], $debug);
  211. if (!$canDisplay) {
  212. return FALSE;
  213. }
  214. /** @var User $currentUser */
  215. $currentUser = $this->security->getUser();
  216. // on recherche l'acl si on peut récupérer son slug data-component-acl
  217. if ( isset($item['data']['data-component-acl'])){
  218. $canDisplay = $this->userIsGranted($currentUser, $item['data']['data-component-acl'] ?? ACL::ACL_NO_COMPONENT);
  219. }
  220. if (!$canDisplay) {
  221. return FALSE;
  222. }
  223. // on regarde s'il y a des univers... on verifie le currentUser car le component peut être utilisé en partie security
  224. if ($currentUser !== NULL && isset($item['univers'])){
  225. if ( $canDisplay && !$currentUser->isDeveloper() && !$currentUser->isSuperAdmin() && isset($item['univers'])){
  226. $canDisplay = $this->aclService->canDisplayByUniverses($currentUser, $item['univers']);
  227. }
  228. }
  229. return $canDisplay;
  230. }
  231. /**
  232. * Défini si un component s'affiche via la clef display
  233. *
  234. * Elle contient 2 enfants:
  235. * enabled_on : array (tableau de route)|null
  236. * disabled_on : array (tableau de route)|null
  237. * Si disabled_on est a autre chose que null, alors c'est lui qui prend l'ascendant
  238. * Afficher partout => enabled_on: null + disabled_on: [] ou null
  239. *
  240. * @param array $display
  241. *
  242. * @return bool
  243. */
  244. private function canDisplayOnPageByConfig(array $display, bool $debug = FALSE)
  245. {
  246. $currentRoute = $this->requestStack->getCurrentRequest()->get( '_route' );
  247. if ($currentRoute === NULL){
  248. return $this->checkWhenRouteIsNull();
  249. }
  250. $canDisplay = TRUE; // par défaut on affiche
  251. // On prend la config du disabled_on si elle n'est pas nulle
  252. $displayByDisabled = FALSE;
  253. if (isset($display['disabled_on']) && $display['disabled_on'] !== NULL){
  254. $displayByDisabled = TRUE;
  255. }
  256. // On prend la config enbaled_on
  257. if (isset($display['enabled_on']) && !$displayByDisabled){
  258. $arrayRoute = $display['enabled_on'];
  259. if(gettype($display['enabled_on']) === "string") {
  260. $arrayRoute = json_decode($display['enabled_on'], true);
  261. }
  262. switch (TRUE){
  263. // tableau vide, ce n'est pas visible
  264. case $arrayRoute === []:
  265. $canDisplay = FALSE;
  266. break;
  267. // NULL ou valeur qui n'est pas un tableau, on considère que c'est visible
  268. // ainsi si enabled_on et disabled_on sont NULL, on affiche
  269. case $arrayRoute === NULL:
  270. case !is_array($arrayRoute):
  271. $canDisplay = TRUE;
  272. break;
  273. // on regarde si la route actuelle est dans le tableau pour l'afficher
  274. default:
  275. $canDisplay = in_array( $currentRoute, $arrayRoute, TRUE );
  276. break;
  277. }
  278. }
  279. // on prend la config disabled_on
  280. if (isset($display['disabled_on']) && $displayByDisabled){
  281. $arrayRoute = $display['disabled_on'];
  282. if(gettype($display['disabled_on']) === "string") {
  283. $arrayRoute = json_decode($display['disabled_on'], true);
  284. }
  285. switch (TRUE){
  286. // tableau vide, c'est visible partout
  287. case $arrayRoute === []:
  288. $canDisplay = TRUE;
  289. break;
  290. // NULL ou valeur qui n'est pas un tableau, on refuse l'affichage
  291. // Normalement on ne tombe pas dans cette configuration puisque $displayByDisabled est FALSE
  292. case $arrayRoute === NULL:
  293. case !is_array($arrayRoute):
  294. $canDisplay = FALSE;
  295. break;
  296. // on regarde si la route actuelle est dans le tableau pour refuser l'affichage
  297. default:
  298. $canDisplay = !in_array( $currentRoute, $arrayRoute, TRUE );
  299. break;
  300. }
  301. }
  302. return $canDisplay;
  303. }
  304. /**
  305. * Permet de savoir si le user peut voir le slider
  306. *
  307. * Les acl du slider sont intégré à l'édition de l'entité
  308. *
  309. * @param User $user
  310. * @param SliderItem $item
  311. *
  312. * @return bool
  313. */
  314. public function canDisplaySliderItem(?User $user, SliderItem $item): bool
  315. {
  316. if(!$user) return FALSE;
  317. $jobs = $item->getDisplayJob();
  318. $universes = $item->getDisplayUniverses();
  319. // Le superadmin et dev doivent pouvoir tout voir...
  320. if ( $user->isDeveloper() || $user->isSuperAdmin() ) {
  321. return TRUE;
  322. }
  323. // Par défaut, tout s'affiche
  324. $canDisplay = TRUE;
  325. // SI config par job on regarde si ça match
  326. if ( $jobs !== NULL && !in_array( $user->getJob(), $jobs, TRUE ) ) {
  327. $canDisplay = FALSE;
  328. }
  329. // Si config par univers on regarde si ça match
  330. if ( $universes !== NULL ) {
  331. foreach ( $user->getUniverses() as $userUnivers ) {
  332. if ( in_array( $userUnivers->getSlug(), $universes, TRUE ) ) {
  333. $canDisplay = TRUE;
  334. break;
  335. }
  336. $canDisplay = FALSE;
  337. }
  338. }
  339. return $canDisplay;
  340. }
  341. /**
  342. * Normalise la transformation de l'array qui contient les params d'une route pour la transformer en string
  343. * @param array $params
  344. *
  345. * @return false|string
  346. * @throws \JsonException
  347. */
  348. public function formatParamsToString(array $params)
  349. {
  350. return $this->aclService->formatArrayParamsToString($params);
  351. }
  352. /**
  353. * Retourne un tableau avec la liste de roles et les jobs relatif à ces roles
  354. *
  355. * @return array[]
  356. */
  357. public function getDefaultRolesAndJobs()
  358. {
  359. return $this->aclService->getDefaultRoleAndJob();
  360. }
  361. /**
  362. * @return UserInterface|null
  363. */
  364. public function getOriginalUser(): ?UserInterface
  365. {
  366. $token = $this->security->getToken();
  367. if ($token instanceof SwitchUserToken)
  368. {
  369. $user = $token->getOriginalToken()->getUser();
  370. $user = $this->em->getRepository(User::class)->findOneBy(['email' => $user->getEmail()]);
  371. return $user;
  372. }
  373. return $this->security->getUser();
  374. }
  375. /**
  376. * @param User $user
  377. * @param string $role
  378. *
  379. * @return bool
  380. */
  381. public function hasRole(User $user, string $role): bool
  382. {
  383. $reachableRoles = $this->roleHierarchy->getReachableRoleNames($user->getRoles());
  384. return in_array($role, $reachableRoles);
  385. }
  386. }