src/Security/Voter/ProjectVoter.php line 20

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Security\Voter;
  3. use InvalidArgumentException;
  4. use MedBrief\MSR\Entity\Account;
  5. use MedBrief\MSR\Entity\Firm;
  6. use MedBrief\MSR\Entity\InterpartyDisclosure;
  7. use MedBrief\MSR\Entity\Project;
  8. use MedBrief\MSR\Entity\User;
  9. use MedBrief\MSR\Service\EntityHelper\UserHelper;
  10. use MedBrief\MSR\Service\Role\RoleParserService;
  11. use MedBrief\MSR\Traits\Security\Authorization\Voter\ClientSortingSessionTrait;
  12. use Override;
  13. use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
  14. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  15. use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
  16. use Symfony\Component\Security\Core\User\UserInterface;
  17. class ProjectVoter implements VoterInterface
  18. {
  19. use ClientSortingSessionTrait;
  20. // CONSTANTS
  21. public const CREATE = 'CREATE';
  22. public const READ = 'READ';
  23. public const UPDATE = 'UPDATE';
  24. public const DELETE = 'DELETE';
  25. public const ADMINISTRATION = 'ADMINISTRATION';
  26. public const MEDICAL_RECORDS_ADMINISTRATION = 'MEDICAL_RECORDS_ADMINISTRATION';
  27. public const RADIOLOGY_ADMINISTRATION = 'RADIOLOGY_ADMINISTRATION';
  28. // Determines if a user may view the "File details" panel for a project document.
  29. // Matter-level Expert, Expert - View Only, Scanner and Scanner - Download Enabled roles are excluded.
  30. public const VIEW_FILE_DETAILS = 'VIEW_FILE_DETAILS';
  31. public const USER_ADMINISTRATION = 'USER_ADMINISTRATION';
  32. public const CLINICAL_SUMMARY_PROJECT_ADMINISTRATION = 'CLINICAL_SUMMARY_PROJECT_ADMINISTRATION';
  33. public const PROJECT_USER_LIST = 'PROJECT_USER_LIST';
  34. public const VIEW_DASHBOARD = 'VIEW_DASHBOARD';
  35. public const TOGGLE_STATUS = 'TOGGLE_STATUS';
  36. public const ARCHIVE = 'ARCHIVE';
  37. public const CANCEL_DELETE = 'CANCEL_DELETE';
  38. public const RADIOLOGY_DOWNLOAD = 'RADIOLOGY_DOWNLOAD';
  39. public const RADIOLOGY_DOWNLOAD_AUDIT_REPORT = 'RADIOLOGY_DOWNLOAD_AUDIT_REPORT';
  40. public const MEDICAL_RECORD_DOWNLOAD = 'MEDICAL_RECORD_DOWNLOAD';
  41. public const DELETION_REPORT_DOWNLOAD = 'DELETION_REPORT_DOWNLOAD';
  42. public const INTERNAL_USER_ACCESS_REPORT_DOWNLOAD = 'INTERNAL_USER_ACCESS_REPORT_DOWNLOAD';
  43. public const CHRONOLOGY_ADMINISTRATION = 'CHRONOLOGY_ADMINISTRATION';
  44. // This permission determines if a user sees an inactive notice message
  45. // when an inactive Project is accessed.
  46. public const BYPASS_INACTIVE_NOTICE = 'BYPASS_INACTIVE_NOTICE';
  47. // This permission determines if a user can view a closed project
  48. public const BYPASS_CLOSED_NOTICE = 'BYPASS_CLOSED_NOTICE';
  49. // Keep these permissions on a Project, as creating any of these
  50. // directly affects a Project.
  51. public const CREATE_BATCH_REQUEST = 'CREATE_BATCH_REQUEST';
  52. public const CREATE_BATCH_REQUEST_SIMPLE = 'CREATE_BATCH_REQUEST_SIMPLE';
  53. public const CREATE_CHRONOLOGY_REQUEST = 'CREATE_CHRONOLOGY_REQUEST';
  54. public const CREATE_ADDITIONAL_REQUEST = 'CREATE_ADDITIONAL_REQUEST';
  55. public const CREATE_EXPERT_REPORT = 'CREATE_EXPERT_REPORT';
  56. public const LIST_EXPERT_REPORTS = 'LIST_EXPERT_REPORTS';
  57. // Sorting Session
  58. public const SORTING_SESSION_LIST = 'SORTING_SESSION_LIST';
  59. public const CREATE_SORTING_SESSION = 'CREATE_SORTING_SESSION';
  60. public const CREATE_SORTING_SESSION_SIMPLE = 'CREATE_SORTING_SESSION_SIMPLE';
  61. // Creating a matter note
  62. public const CREATE_MATTER_NOTE = 'CREATE_MATTER_NOTE';
  63. // Allow list of matter notes
  64. public const LIST_MATTER_NOTES = 'LIST_MATTER_NOTES';
  65. // Allow view of matter communications
  66. public const VIEW_MATTER_COMMUNICATIONS = 'VIEW_MATTER_COMMUNICATIONS';
  67. // Disclosure permissions
  68. public const MEDICAL_RECORDS_DISCLOSE = 'MEDICAL_RECORDS_DISCLOSE';
  69. public const DISCLOSE_DISC = 'DISCLOSE_DISC';
  70. public const BYPASS_AUTHENTICATION = 'BYPASS_AUTHENTICATION';
  71. // Allows changing the account value of a Matter
  72. public const CHANGE_ACCOUNT = 'CHANGE_ACCOUNT';
  73. // Allows user to create a record request letter
  74. public const MANAGE_REQUEST_LETTERS = 'MANAGE_REQUEST_LETTERS';
  75. // Allows a user to see the 'Unsorted' records for a Project
  76. public const VIEW_UNSORTED_RECORDS = 'VIEW_UNSORTED_RECORDS';
  77. // Project Closure Permissions
  78. public const CREATE_PROJECT_CLOSURE = 'CREATE_PROJECT_CLOSURE';
  79. public const DOWNLOAD_ALL_PROJECT_FILES = 'DOWNLOAD_ALL_PROJECT_FILES';
  80. public const DOWNLOAD_PROJECT_CLOSURE_REPORT = 'DOWNLOAD_PROJECT_CLOSURE_REPORT';
  81. // Allows the user to see a modal showing important notes on the matter, if any.
  82. public const VIEW_IMPORTANT_NOTES = 'VIEW_IMPORTANT_NOTES';
  83. // Allows the user to see the service request requirement banners on the matter dashboard.
  84. public const VIEW_SERVICE_REQUEST_REQUIREMENT_BANNERS = 'VIEW_SERVICE_REQUEST_REQUIREMENT_BANNERS';
  85. // Inter-party Disclosure
  86. public const CREATE_INTERPARTY_DISCLOSURE = 'CREATE_INTERPARTY_DISCLOSURE';
  87. public const LIST_INTERPARTY_DISCLOSURE = 'LIST_INTERPARTY_DISCLOSURE';
  88. // Allows by passing the disabled state of service requests when a matter/project is closed or in the process of being closed.
  89. public const BYPASS_SERVICE_REQUEST_DISABLED = 'BYPASS_SERVICE_REQUEST_DISABLED';
  90. // Allows the user to download the 'user download medical records viewed report'
  91. public const VIEW_THIRD_PARTY_ACCESS_REPORT = 'VIEW_THIRD_PARTY_ACCESS_REPORT';
  92. public function __construct(private AuthorizationCheckerInterface $authorizationChecker, private UserHelper $userHelper)
  93. {
  94. }
  95. /**
  96. * Whether or not this User is allowed to perform specific actions on this Entity
  97. *
  98. * @param mixed $attribute
  99. */
  100. public function supportsAttribute(mixed $attribute): bool
  101. {
  102. return in_array($attribute, [
  103. self::CREATE,
  104. self::READ,
  105. self::UPDATE,
  106. self::DELETE,
  107. self::ADMINISTRATION,
  108. self::MEDICAL_RECORDS_ADMINISTRATION,
  109. self::RADIOLOGY_ADMINISTRATION,
  110. self::USER_ADMINISTRATION,
  111. self::VIEW_DASHBOARD,
  112. self::TOGGLE_STATUS,
  113. self::PROJECT_USER_LIST,
  114. self::RADIOLOGY_DOWNLOAD,
  115. self::RADIOLOGY_DOWNLOAD_AUDIT_REPORT,
  116. self::MEDICAL_RECORD_DOWNLOAD,
  117. self::DELETION_REPORT_DOWNLOAD,
  118. self::INTERNAL_USER_ACCESS_REPORT_DOWNLOAD,
  119. self::CHRONOLOGY_ADMINISTRATION,
  120. self::ARCHIVE,
  121. self::CANCEL_DELETE,
  122. self::BYPASS_INACTIVE_NOTICE,
  123. self::BYPASS_CLOSED_NOTICE,
  124. self::CREATE_BATCH_REQUEST,
  125. self::CREATE_BATCH_REQUEST_SIMPLE,
  126. self::CREATE_CHRONOLOGY_REQUEST,
  127. self::CREATE_ADDITIONAL_REQUEST,
  128. self::SORTING_SESSION_LIST,
  129. self::CREATE_SORTING_SESSION,
  130. self::CREATE_SORTING_SESSION_SIMPLE,
  131. self::CREATE_MATTER_NOTE,
  132. self::LIST_MATTER_NOTES,
  133. self::VIEW_MATTER_COMMUNICATIONS,
  134. self::MEDICAL_RECORDS_DISCLOSE,
  135. self::DISCLOSE_DISC,
  136. self::BYPASS_AUTHENTICATION,
  137. self::CHANGE_ACCOUNT,
  138. self::MANAGE_REQUEST_LETTERS,
  139. self::VIEW_UNSORTED_RECORDS,
  140. self::CREATE_PROJECT_CLOSURE,
  141. self::DOWNLOAD_ALL_PROJECT_FILES,
  142. self::DOWNLOAD_PROJECT_CLOSURE_REPORT,
  143. self::VIEW_IMPORTANT_NOTES,
  144. self::VIEW_SERVICE_REQUEST_REQUIREMENT_BANNERS,
  145. self::CREATE_INTERPARTY_DISCLOSURE,
  146. self::LIST_INTERPARTY_DISCLOSURE,
  147. self::BYPASS_SERVICE_REQUEST_DISABLED,
  148. self::VIEW_THIRD_PARTY_ACCESS_REPORT,
  149. self::CLINICAL_SUMMARY_PROJECT_ADMINISTRATION,
  150. self::CREATE_EXPERT_REPORT,
  151. self::LIST_EXPERT_REPORTS,
  152. self::VIEW_FILE_DETAILS,
  153. ]);
  154. }
  155. /**
  156. * Whether or not this is a supported Class
  157. *
  158. * @param string $class
  159. */
  160. public function supportsClass($class): bool
  161. {
  162. $supportedClass = Project::class;
  163. return $supportedClass === $class || is_subclass_of($class, $supportedClass);
  164. }
  165. /**
  166. * @param Project $entity
  167. *
  168. * @return int
  169. */
  170. #[Override]
  171. public function vote(TokenInterface $token, $entity, array $attributes)
  172. {
  173. /**
  174. * START: This is common code for all Voter::vote() methods
  175. */
  176. // check if class of this object is supported by this voter
  177. if (!$this->supportsClass($entity && !is_array($entity) ? $entity::class : '')) {
  178. return VoterInterface::ACCESS_ABSTAIN;
  179. }
  180. // check if the voter is used correct, only allow one attribute
  181. // this isn't a requirement, it's just one easy way for you to
  182. // design your voter
  183. if (1 !== count($attributes)) {
  184. throw new InvalidArgumentException(
  185. 'Only one attribute is allowed for medbrief Voters.'
  186. );
  187. }
  188. // set the attribute to check against
  189. $attribute = $attributes[0];
  190. // check if the given attribute is covered by this voter
  191. if (!$this->supportsAttribute($attribute)) {
  192. return VoterInterface::ACCESS_ABSTAIN;
  193. }
  194. // get current logged in user
  195. /** @var User $user */
  196. $user = $token->getUser();
  197. // make sure there is a user object (i.e. that the user is logged in)
  198. if (!$user instanceof UserInterface) {
  199. return VoterInterface::ACCESS_DENIED;
  200. }
  201. // Only allow Super Admins and Admins to change accounts
  202. if ($attribute === self::CHANGE_ACCOUNT && !$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
  203. return VoterInterface::ACCESS_DENIED;
  204. }
  205. // Only allow Super Admins to delete a project
  206. if ($attribute === self::DELETE && !$this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN')) {
  207. return VoterInterface::ACCESS_DENIED;
  208. }
  209. // Only super admins can update service requests when the project is in a closed or closing state
  210. if ($attribute === self::BYPASS_SERVICE_REQUEST_DISABLED) {
  211. if ($this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN') === true) {
  212. return VoterInterface::ACCESS_GRANTED;
  213. }
  214. return VoterInterface::ACCESS_DENIED;
  215. }
  216. /**
  217. * Clinical Summary Access Control with project entity passed in as the subject
  218. *
  219. * We need to put this before we grant admin users rights to everything otherwise the
  220. * isCloseInProgressOrComplete check has no effect
  221. */
  222. if ($attribute === self::CLINICAL_SUMMARY_PROJECT_ADMINISTRATION) {
  223. if ($this->canAccessClinicalSummaryWizard($entity)) {
  224. return VoterInterface::ACCESS_GRANTED;
  225. }
  226. return VoterInterface::ACCESS_DENIED;
  227. }
  228. /**
  229. * Deny access to expert report actions if the project does not have a matter request associated with it (Classic matter).
  230. */
  231. if (in_array($attribute, [self::CREATE_EXPERT_REPORT,self::LIST_EXPERT_REPORTS]) && $entity instanceof Project && $entity->getMatterRequest() == null) {
  232. return VoterInterface::ACCESS_DENIED;
  233. }
  234. // Admin users can do everything
  235. if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
  236. return VoterInterface::ACCESS_GRANTED;
  237. }
  238. /**
  239. * END: Common code for all Voter:vote() methods. Put custom logic below.
  240. */
  241. /**
  242. * API (Firm) Access Control
  243. */
  244. if ($user instanceof Firm) {
  245. // If the account that belongs to the project is allocated to the firm's client areas, allow everything.
  246. if ($entity->getAccount() instanceof Account && $user->getClientAreas()->contains($entity->getAccount()) === true) {
  247. return self::ACCESS_GRANTED;
  248. }
  249. return self::ACCESS_DENIED;
  250. }
  251. /**
  252. * Disclosure matter access control
  253. */
  254. // Grab all project levels roles related to this project.
  255. $allProjectRoles = RoleParserService::getAllRolesForProject($entity->getId());
  256. // Users that have been directly invited to the Disclosure will have permission granted
  257. $isDirectlyInvitedToDisclosureMatter = array_filter($allProjectRoles, fn ($role) => $this->authorizationChecker->isGranted($role)) !== [];
  258. // Check if the project is a disclosure, and exclude anyone who has been directly invited to the disclosure matter (i.e. those that were
  259. // added when creating the disclosure). Project level project managers of the original project will not have a project role on the disclosure target project.
  260. if ($entity->isTypeDisclosure() && $isDirectlyInvitedToDisclosureMatter === false) {
  261. $allowedAttributes = [
  262. self::READ,
  263. self::RADIOLOGY_DOWNLOAD,
  264. self::MEDICAL_RECORD_DOWNLOAD,
  265. self::BYPASS_INACTIVE_NOTICE,
  266. self::VIEW_THIRD_PARTY_ACCESS_REPORT,
  267. ];
  268. // Disclosure matters only allow certain actions for the those who can VIEW the source disclosure entity
  269. if (in_array($attribute, $allowedAttributes) && $entity->getDisclosureSources()->count() > 0) {
  270. // Grant access if the user has access to VIEW the original source disclosure (we take the latest one in the chain of sources)
  271. /** @var InterpartyDisclosure $disclosure */
  272. $disclosure = $entity->getDisclosureSources()->last();
  273. if ($this->authorizationChecker->isGranted(InterpartyDisclosureVoter::VIEW, $disclosure)) {
  274. return self::ACCESS_GRANTED;
  275. }
  276. }
  277. return self::ACCESS_DENIED;
  278. }
  279. //Checks if user can download radiology audit report
  280. if ($attribute === self::RADIOLOGY_DOWNLOAD_AUDIT_REPORT) {
  281. return $this->canRadiologyDownloadAuditReport($entity);
  282. }
  283. if ($attribute === self::LIST_EXPERT_REPORTS && $this->canViewExpertReports($entity, $user)) {
  284. return VoterInterface::ACCESS_GRANTED;
  285. }
  286. $this->userHelper->setUser($user);
  287. $denyAccess = [
  288. self::CREATE_SORTING_SESSION,
  289. self::LIST_MATTER_NOTES,
  290. self::CREATE_MATTER_NOTE,
  291. self::MEDICAL_RECORDS_DISCLOSE,
  292. self::DISCLOSE_DISC,
  293. self::VIEW_MATTER_COMMUNICATIONS,
  294. self::DELETION_REPORT_DOWNLOAD,
  295. self::INTERNAL_USER_ACCESS_REPORT_DOWNLOAD,
  296. self::MANAGE_REQUEST_LETTERS,
  297. self::DELETE,
  298. self::VIEW_IMPORTANT_NOTES,
  299. self::VIEW_SERVICE_REQUEST_REQUIREMENT_BANNERS,
  300. self::LIST_EXPERT_REPORTS,
  301. ];
  302. // Deny all other roles these permissions
  303. if (in_array($attribute, $denyAccess)) {
  304. return VoterInterface::ACCESS_DENIED;
  305. }
  306. // Permissions related to the creation and management of sorting sessions and batches.
  307. $attributeGroup = [
  308. self::CREATE_BATCH_REQUEST_SIMPLE,
  309. self::SORTING_SESSION_LIST,
  310. self::CREATE_SORTING_SESSION_SIMPLE,
  311. ];
  312. if (in_array($attribute, $attributeGroup)) {
  313. if ($this->hasClientSessionAccess($entity, $user)) {
  314. return VoterInterface::ACCESS_GRANTED;
  315. };
  316. return VoterInterface::ACCESS_DENIED;
  317. }
  318. // Deny all other roles from creating these ServiceRequests
  319. $serviceRequestCreateAttributes = [
  320. self::CREATE_BATCH_REQUEST,
  321. self::CREATE_CHRONOLOGY_REQUEST,
  322. self::CREATE_ADDITIONAL_REQUEST,
  323. self::CREATE_EXPERT_REPORT,
  324. ];
  325. if (in_array($attribute, $serviceRequestCreateAttributes)) {
  326. return VoterInterface::ACCESS_DENIED;
  327. }
  328. if ($attribute === self::VIEW_DASHBOARD && $user->getMatterDashboardEnabled()) {
  329. // then they have access to do anything
  330. return VoterInterface::ACCESS_GRANTED;
  331. }
  332. // if this user is a Super Administrator for the Account for which this Project belongs
  333. if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $entity->getAccount()->getId() . '_SUPERADMINISTRATOR')) {
  334. // then they have access to do anything, except delete.
  335. return VoterInterface::ACCESS_GRANTED;
  336. }
  337. // Otherwise if this user is a Client Administrator for the Account then they can
  338. // do everything else except for User Administration and Deletion.
  339. if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $entity->getAccount()->getId() . '_ADMINISTRATOR')) {
  340. // Commenting this out for now because Kennedy's actually need
  341. // regular Client Administrators to still have this access for now. - RR
  342. //if ($attribute != self::USER_ADMINISTRATION) {
  343. // then they have access
  344. return VoterInterface::ACCESS_GRANTED;
  345. //}
  346. }
  347. // if this user is a Sorter for the Account for which this Project belongs
  348. // if we are looking for access other than delete, full administration and user management abilities
  349. if ($this->authorizationChecker->isGranted('ROLE_ACCOUNT_' . $entity->getAccount()->getId() . '_SORTER') && ($attribute != self::DELETE
  350. && $attribute != self::ADMINISTRATION
  351. && $attribute != self::PROJECT_USER_LIST
  352. && $attribute != self::USER_ADMINISTRATION
  353. && $attribute != self::CREATE_PROJECT_CLOSURE
  354. && $attribute != self::CANCEL_DELETE
  355. && $attribute != self::ARCHIVE
  356. && $attribute != self::DOWNLOAD_ALL_PROJECT_FILES
  357. && $attribute != self::DOWNLOAD_PROJECT_CLOSURE_REPORT
  358. && $attribute != self::CREATE_INTERPARTY_DISCLOSURE
  359. && $attribute != self::LIST_INTERPARTY_DISCLOSURE)) {
  360. // then account level sorters have this access
  361. return VoterInterface::ACCESS_GRANTED;
  362. }
  363. // if we are looking for any access other than delete and full administration.
  364. // Note: Project managers ARE allowed closure-related permissions (CREATE_PROJECT_CLOSURE,
  365. // CANCEL_DELETE, ARCHIVE, DOWNLOAD_ALL_PROJECT_FILES, DOWNLOAD_PROJECT_CLOSURE_REPORT)
  366. // to match client admin + super admin access. See MSR-5782.
  367. if ($attribute != self::DELETE
  368. && $attribute != self::ADMINISTRATION
  369. ) {
  370. // then project managers may do this
  371. if ($this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_PROJECTMANAGER')) {
  372. return VoterInterface::ACCESS_GRANTED;
  373. }
  374. // Grant project manager access to a Project's manager, which will likely be a
  375. // ACCOUNT_PROJECT_MANAGER
  376. if ($entity->getManager() && $entity->getManager()->getId() === $user->getId()) {
  377. return VoterInterface::ACCESS_GRANTED;
  378. }
  379. }
  380. // if we are looking for the medical records administration or radiology administration attribute
  381. if ($attribute == self::MEDICAL_RECORDS_ADMINISTRATION || $attribute == self::RADIOLOGY_ADMINISTRATION || $attribute === self::VIEW_UNSORTED_RECORDS) {
  382. // any of the following roles will grant access
  383. $allowedRoles = [
  384. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  385. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  386. 'ROLE_PROJECT_' . $entity->getId() . '_PROJECTMANAGER',
  387. ];
  388. // so if the user has any one of these
  389. foreach ($allowedRoles as $role) {
  390. if ($this->authorizationChecker->isGranted($role)) {
  391. // then they have access
  392. return VoterInterface::ACCESS_GRANTED;
  393. }
  394. }
  395. }
  396. // If the project allows experts to see the unsorted records, and the user has an export role on the project.
  397. if ($attribute === self::VIEW_UNSORTED_RECORDS && ($entity->getAllowExpertViewUnsortedRecords() === true
  398. && ($this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_EXPERT') || $this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_EXPERTVIEWER')))) {
  399. return VoterInterface::ACCESS_GRANTED;
  400. }
  401. // Certain roles will allow you to bypass the inactive notice on an inactive Project's related controller.
  402. if ($attribute == self::BYPASS_INACTIVE_NOTICE) {
  403. // any of the following roles will grant access
  404. $allowedRoles = [
  405. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  406. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  407. 'ROLE_PROJECT_' . $entity->getId() . '_PROJECTMANAGER',
  408. ];
  409. // so if the user has any one of these
  410. foreach ($allowedRoles as $role) {
  411. if ($this->authorizationChecker->isGranted($role)) {
  412. // then they have access
  413. return VoterInterface::ACCESS_GRANTED;
  414. }
  415. }
  416. }
  417. // Certain roles will allow you to bypass the closed notice on a closed Project's related controller.
  418. if ($attribute == self::BYPASS_CLOSED_NOTICE) {
  419. // any of the following roles will grant access
  420. $allowedRoles = [
  421. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  422. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  423. 'ROLE_PROJECT_' . $entity->getId() . '_PROJECTMANAGER',
  424. ];
  425. // so if the user has any one of these
  426. foreach ($allowedRoles as $role) {
  427. if ($this->authorizationChecker->isGranted($role)) {
  428. // then they have access
  429. return VoterInterface::ACCESS_GRANTED;
  430. }
  431. }
  432. }
  433. if ($attribute == self::RADIOLOGY_DOWNLOAD || $attribute == self::MEDICAL_RECORD_DOWNLOAD) {
  434. // Experts may do this, but EXPERTVIEWER's may not
  435. if ($this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_EXPERT')) {
  436. return VoterInterface::ACCESS_GRANTED;
  437. }
  438. // Scanner - Download Enabled may do this, but Scanner's may not
  439. if ($attribute == self::MEDICAL_RECORD_DOWNLOAD && $this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD')) {
  440. return VoterInterface::ACCESS_GRANTED;
  441. }
  442. // Certain roles may have access to Radiology
  443. if ($attribute == self::RADIOLOGY_DOWNLOAD) {
  444. // Array of permitted/allowed roles
  445. $allowedRoles = [
  446. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  447. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  448. ];
  449. // Iterate through them and...
  450. foreach ($allowedRoles as $role) {
  451. // ... if they have the role...
  452. if ($this->authorizationChecker->isGranted($role)) {
  453. // SHAZAM!
  454. return VoterInterface::ACCESS_GRANTED;
  455. }
  456. }
  457. }
  458. }
  459. // These project-level roles must not view the "File details" panel.
  460. // Any user reaching this block without one of these roles is granted access.
  461. if ($attribute === self::VIEW_FILE_DETAILS) {
  462. $excludedRoles = [
  463. 'ROLE_PROJECT_' . $entity->getId() . '_EXPERT',
  464. 'ROLE_PROJECT_' . $entity->getId() . '_EXPERTVIEWER',
  465. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  466. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  467. ];
  468. foreach ($excludedRoles as $role) {
  469. if ($this->authorizationChecker->isGranted($role)) {
  470. return VoterInterface::ACCESS_DENIED;
  471. }
  472. }
  473. return VoterInterface::ACCESS_GRANTED;
  474. }
  475. // if the user is wanting to read information about this Project
  476. if ($attribute == self::READ) {
  477. // if the user is an expert agency administrator let them pass.
  478. // this should probably be done in a better way, tying permissions
  479. // to an entity in one place somehow....
  480. if ($user->isExpertAgencyAdministrator()) {
  481. return VoterInterface::ACCESS_GRANTED;
  482. }
  483. // if the user has any role related to this project
  484. if ($this->userHelper->hasProjectRole($entity)) {
  485. // then they have access
  486. return VoterInterface::ACCESS_GRANTED;
  487. }
  488. }
  489. // if we are looking for updating a project
  490. // if the user is a project manager
  491. if ($attribute == self::UPDATE && $this->authorizationChecker->isGranted('ROLE_PROJECT_' . $entity->getId() . '_PROJECTMANAGER')) {
  492. // then they have access
  493. return VoterInterface::ACCESS_GRANTED;
  494. }
  495. // if we are checking to see if we can bypass authentication
  496. if ($attribute == self::BYPASS_AUTHENTICATION && $this->authorizationChecker->isGranted('USER_ADMINISTRATION', $entity)) {
  497. return VoterInterface::ACCESS_GRANTED;
  498. }
  499. // If we get to the end of this function, then no decisions have been
  500. // made so we deny access
  501. return VoterInterface::ACCESS_DENIED;
  502. }
  503. /**
  504. * Checks whether a user can view expert reports for a given project.
  505. *
  506. * @param Project $project
  507. * @param UserInterface $user
  508. *
  509. * @return bool
  510. */
  511. private function canViewExpertReports(Project $project, UserInterface $user): bool
  512. {
  513. $account = $project->getAccount();
  514. $matterRequest = $project->getMatterRequest();
  515. // If there is no matter request associated with the project, deny access
  516. if ($matterRequest == null) {
  517. return false;
  518. }
  519. if (!$account->isMatchOptInAllClientUsers()
  520. && (!$account->isMatchOptInSpecificUsers() || !$user instanceof User || !$user->getMatchOptIn())) {
  521. return false;
  522. }
  523. $accountId = $account->getId();
  524. $projectId = $project->getId();
  525. $allowedRoles = [
  526. 'ROLE_ACCOUNT_' . $accountId . '_ADMINISTRATOR',
  527. 'ROLE_ACCOUNT_' . $accountId . '_SUPERADMINISTRATOR',
  528. 'ROLE_ACCOUNT_' . $accountId . '_PROJECTMANAGER',
  529. 'ROLE_PROJECT_' . $projectId . '_PROJECTMANAGER',
  530. 'ROLE_PROJECT_' . $projectId . '_EXPERT',
  531. ];
  532. foreach ($allowedRoles as $role) {
  533. if ($this->authorizationChecker->isGranted($role)) {
  534. return true;
  535. }
  536. }
  537. return $project->getManager() === $user;
  538. }
  539. /**
  540. * Checks whether user has administrative rights for a clinical summary
  541. *
  542. *
  543. *
  544. * @param Project $entity
  545. *
  546. * @return int
  547. */
  548. private function canAccessClinicalSummaryWizard(Project $entity)
  549. {
  550. // Deny access if the project is in the process of being closed or is closed
  551. if ($entity->isCloseInProgressOrComplete()) {
  552. return false;
  553. }
  554. // Allow access if the user has the ROLE_ADMIN or ROLE_SUPER_ADMIN role
  555. return $this->authorizationChecker->isGranted('ROLE_ADMIN');
  556. }
  557. /**
  558. * Checks whether user can download radiology audit report
  559. *
  560. * @todo: Not type hinting this method now as this may change later
  561. *
  562. * @param Project $entity
  563. */
  564. private function canRadiologyDownloadAuditReport(Project $entity): int
  565. {
  566. $deniedRoles = [
  567. 'ROLE_PROJECT_' . $entity->getId() . '_EXPERT',
  568. 'ROLE_PROJECT_' . $entity->getId() . '_EXPERTVIEWER',
  569. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNER',
  570. 'ROLE_PROJECT_' . $entity->getId() . '_SCANNERDOWNLOAD',
  571. ];
  572. foreach ($deniedRoles as $role) {
  573. if ($this->authorizationChecker->isGranted($role)) {
  574. return VoterInterface::ACCESS_DENIED;
  575. }
  576. }
  577. return VoterInterface::ACCESS_GRANTED;
  578. }
  579. }