src/Security/Voter/MatchVoter.php line 16

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Security\Voter;
  3. use MedBrief\MSR\Entity\MessageThread;
  4. use MedBrief\MSR\Entity\MessageThreadParticipant;
  5. use MedBrief\MSR\Entity\Project;
  6. use MedBrief\MSR\Repository\MessageThreadParticipantRepository;
  7. use MedBrief\MSR\Repository\MessageThreadRepository;
  8. use Override;
  9. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  10. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  11. use Symfony\Component\Security\Core\Authorization\Voter\Voter;
  12. use Symfony\Component\Security\Core\User\UserInterface;
  13. class MatchVoter extends Voter
  14. {
  15. public const VIEW_FIRM = 'MATCH_VIEW_FIRM';
  16. public const VIEW_EXPERT = 'MATCH_VIEW_EXPERT';
  17. public function __construct(
  18. private readonly AuthorizationCheckerInterface $auth,
  19. private readonly MessageThreadRepository $messageThreadRepository,
  20. private readonly MessageThreadParticipantRepository $messageThreadParticipantRepository,
  21. ) {
  22. }
  23. #[Override]
  24. protected function supports(string $attribute, mixed $subject): bool
  25. {
  26. return \in_array($attribute, [self::VIEW_FIRM, self::VIEW_EXPERT], true)
  27. && $subject instanceof Project;
  28. }
  29. #[Override]
  30. protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
  31. {
  32. $user = $token->getUser();
  33. if (!$user instanceof UserInterface) {
  34. return false;
  35. }
  36. /** @var Project $project */
  37. $project = $subject;
  38. return match ($attribute) {
  39. self::VIEW_FIRM => $this->canViewAsFirm($user, $project),
  40. self::VIEW_EXPERT => $this->canViewAsExpert($user, $project),
  41. default => false,
  42. };
  43. }
  44. /**
  45. * Determines if the user can view the match tab.
  46. * User roles allowed:
  47. * - MB super admins (ROLE_SUPER_ADMIN)
  48. * - MB admins (ROLE_ADMIN)
  49. * - Client super admins (ROLE_ACCOUNT_{accountId}_SUPERADMINISTRATOR)
  50. * - Client admins (ROLE_ACCOUNT_{accountId}_ADMINISTRATOR)
  51. * - Matter/Project managers:
  52. * - Role-based: ROLE_PROJECT_{projectId}_PROJECTMANAGER
  53. * - Entity-based: $project->getManager() === $user
  54. *
  55. * @param UserInterface $user
  56. * @param Project $project
  57. *
  58. * @return bool
  59. */
  60. private function canViewAsFirm(UserInterface $user, Project $project): bool
  61. {
  62. // Check if client has opted in to match and if the matter is of type Clinical Negligence and is a client matter, if classic matter, don't allow viewing of match
  63. if (!$project->getAccount()->getMatchOptIn()
  64. || $project->getMatterType() !== Project::MATTER_TYPE_CLINICAL_NEGLIGENCE
  65. || $project->getType() !== Project::TYPE_MATTER_FIRM
  66. || $project->getMatterRequest() === null
  67. ) {
  68. return false;
  69. }
  70. // MB admins
  71. if ($this->auth->isGranted('ROLE_SUPER_ADMIN') || $this->auth->isGranted('ROLE_ADMIN')) {
  72. return true;
  73. }
  74. // Client-level admins
  75. if ($this->auth->isGranted('ROLE_ACCOUNT_' . $project->getAccount()->getId() . '_SUPERADMINISTRATOR')) {
  76. return true;
  77. }
  78. if ($this->auth->isGranted('ROLE_ACCOUNT_' . $project->getAccount()->getId() . '_ADMINISTRATOR')) {
  79. return true;
  80. }
  81. // Matter/Project manager (role-based)
  82. if ($this->auth->isGranted('ROLE_PROJECT_' . $project->getId() . '_PROJECTMANAGER')) {
  83. return true;
  84. }
  85. // Matter/Project manager (entity relationship fallback)
  86. if ($project->getManager() && $project->getManager() === $user) {
  87. return true;
  88. }
  89. return false;
  90. }
  91. /**
  92. * Scaffolding in case we want to limit which users can message experts.
  93. *
  94. * @param UserInterface $user
  95. * @param Project $project
  96. *
  97. * @return bool
  98. */
  99. private function canViewAsExpert(UserInterface $user, Project $project): bool
  100. {
  101. // Invited Experts
  102. if ($this->auth->isGranted('ROLE_PROJECT_' . $project->getAccount()->getId() . '_EXPERT')) {
  103. return true;
  104. }
  105. if ($this->auth->isGranted('ROLE_PROJECT_' . $project->getAccount()->getId() . '_EXPERTVIEWER')) {
  106. return true;
  107. }
  108. // Uninvited Experts can only view if they are participants in the message thread for the project
  109. $thread = $this->messageThreadRepository->findOneBy(['project' => $project]) ?? null;
  110. if ($thread instanceof MessageThread) {
  111. $threadParticipant = $this->messageThreadParticipantRepository->findParticipant($thread, $user);
  112. if ($threadParticipant instanceof MessageThreadParticipant) {
  113. return true;
  114. }
  115. }
  116. return false;
  117. }
  118. }