src/Security/Voter/PDFTronAnnotationVoter.php line 16

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Security\Voter;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use InvalidArgumentException;
  5. use MedBrief\MSR\Entity\Annotation;
  6. use MedBrief\MSR\Entity\Document;
  7. use MedBrief\MSR\Entity\User;
  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\VoterInterface;
  12. use Symfony\Component\Security\Core\User\UserInterface;
  13. class PDFTronAnnotationVoter implements VoterInterface
  14. {
  15. public const CREATE = 'CREATE';
  16. public const READ = 'READ';
  17. public const UPDATE = 'UPDATE';
  18. public const DELETE = 'DELETE';
  19. public const ADMINISTRATION = 'ADMINISTRATION';
  20. public const UPDATE_VISIBILITY = 'UPDATE_VISIBILITY';
  21. public function __construct(private readonly AuthorizationCheckerInterface $authorizationChecker, private readonly EntityManagerInterface $entityManager)
  22. {
  23. }
  24. public function supportsAttribute($attribute): bool
  25. {
  26. return in_array($attribute, [
  27. self::CREATE,
  28. self::READ,
  29. self::UPDATE,
  30. self::DELETE,
  31. self::ADMINISTRATION,
  32. self::UPDATE_VISIBILITY,
  33. ]);
  34. }
  35. public function supportsClass($class): bool
  36. {
  37. $supportedClass = Annotation::class;
  38. return $supportedClass === $class || is_subclass_of($class, $supportedClass);
  39. }
  40. /**
  41. *
  42. * @param mixed $entity
  43. */
  44. #[Override]
  45. public function vote(TokenInterface $token, $entity, array $attributes)
  46. {
  47. /**
  48. * START: This is common code for all Voter::vote() methods
  49. */
  50. // check if class of this object is supported by this voter
  51. if (!$this->supportsClass($entity && !is_array($entity) ? $entity::class : '')) {
  52. return VoterInterface::ACCESS_ABSTAIN;
  53. }
  54. // check if the voter is used correct, only allow one attribute
  55. // this isn't a requirement, it's just one easy way for you to
  56. // design your voter
  57. if (1 !== count($attributes)) {
  58. throw new InvalidArgumentException(
  59. 'Only one attribute is allowed for medbrief Voters.'
  60. );
  61. }
  62. // set the attribute to check against
  63. $attribute = $attributes[0];
  64. // check if the given attribute is covered by this voter
  65. if (!$this->supportsAttribute($attribute)) {
  66. return VoterInterface::ACCESS_ABSTAIN;
  67. }
  68. // get current logged in user
  69. /** @var User $user */
  70. $user = $token->getUser();
  71. // make sure there is a user object (i.e. that the user is logged in)
  72. if (!$user instanceof UserInterface) {
  73. return VoterInterface::ACCESS_DENIED;
  74. }
  75. // Admin and Super admin users can do everything.
  76. if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
  77. return VoterInterface::ACCESS_GRANTED;
  78. }
  79. /**
  80. * END: Common code for all Voter:vote() methods. Put custom logic below.
  81. */
  82. // Check if the user has access to the Document in order to determine if they can see the annotations.
  83. if ($entity->getDocumentId()) {
  84. // Retrieve the Document...
  85. /** @var Document $document */
  86. $document = $this->entityManager->getRepository(Document::class)->find($entity->getDocumentId());
  87. // You need to be able to read the Project the Document belongs to, and you stream the native file for the Document,
  88. // to do anything related to the Document's annotations.
  89. if ($document && $document->getProject() && ($this->authorizationChecker->isGranted('READ', $document->getProject())
  90. && $this->authorizationChecker->isGranted('STREAM_NATIVE', $document))) {
  91. // The basic permissions for a Document is enough to allow CREATE of annotations.
  92. if ($attribute === self::CREATE) {
  93. return VoterInterface::ACCESS_GRANTED;
  94. }
  95. // Reading annotations is based on the visibility of the Annotation, unless the user has Client level access,
  96. // or is a Project manager for the Project the annotation's document belongs to.
  97. if ($attribute === self::READ) {
  98. // For disclosure matters, only annotations made by the user can be seen by them.
  99. if ($document->getProject()->isTypeDisclosure() === true) {
  100. if ($entity->getAuthorId() == $user->getId()) {
  101. return VoterInterface::ACCESS_GRANTED;
  102. }
  103. } elseif ($entity->isPubliclyVisible()) {
  104. // If the annotation is public, everyone can see it.
  105. return VoterInterface::ACCESS_GRANTED;
  106. } elseif ($entity->getAuthorId() == $user->getId()) {
  107. // If the user is the author of the annotation, they can see it.
  108. return VoterInterface::ACCESS_GRANTED;
  109. } elseif ($user->isAccountAdministrator()) {
  110. // If the user is an account level administrator, they can see all annotations.
  111. return VoterInterface::ACCESS_GRANTED;
  112. } elseif ($this->authorizationChecker->isGranted('ROLE_PROJECT_' . $document->getProject()->getId() . '_PROJECTMANAGER')) {
  113. // If the user is a project manager for the Matter the document belongs to, they can see all annotations.
  114. return VoterInterface::ACCESS_GRANTED;
  115. }
  116. }
  117. // In order to UPDATE or DELETE an annotation, you must be the Annotation's Author.
  118. if (($attribute === self::UPDATE || $attribute === self::DELETE) && $entity->getAuthorId() == $user->getId()) {
  119. return VoterInterface::ACCESS_GRANTED;
  120. }
  121. // Check if the user can update the visibility of an annotation.
  122. if ($attribute === self::UPDATE_VISIBILITY && $this->authorizationChecker->isGranted('UPDATE_ANNOTATION_VISIBILITY', $document)) {
  123. return VoterInterface::ACCESS_GRANTED;
  124. }
  125. }
  126. }
  127. // If we get to the end of this function, then no decisions have been
  128. // made so we deny access
  129. return VoterInterface::ACCESS_DENIED;
  130. }
  131. }