<?php
namespace App\Security\Voter\UserRequest;
use App\Entity\User;
use App\Manager\UserRightsManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use WebServiceCollectionBundle\Model\AtlasPce\UserRequestRequestContext;
use WebServiceCollectionBundle\Model\AtlasPce\UserRequestRequestModel;
class UserRequestVoter extends Voter
{
public const VIEW_REQUEST_ATTR = 'VIEW_REQUEST';
/**
* @var LoggerInterface
*/
protected $logger;
public function __construct(
LoggerInterface $logger
) {
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function supports($attribute, $subject)
{
$isView = static::VIEW_REQUEST_ATTR === $attribute;
$isSubjectUrModel = ($subject instanceof UserRequestRequestModel);
return $isView && $isSubjectUrModel;
}
/**
* {@inheritdoc}
*/
public function voteOnAttribute($attribute, $subject, TokenInterface $token)
{
$user = $token->getUser();
switch ($attribute) {
case static::VIEW_REQUEST_ATTR:
return $this->mayView($subject, $user);
}
throw new \LogicException('This code should not be reached!');
}
/**
* Test if a user is allowed to view a request.
*
* @param UserRequestRequestModel $subject
* @param User $tokenAttributes
*
* @return bool
*/
protected function mayView(UserRequestRequestModel $request, User $user): bool
{
$propertyAccessor = PropertyAccess::createPropertyAccessor();
$userRoles = $user->getRoles();
$userIsAdminOrSuezOrManager = (0 < count(array_intersect(
$userRoles,
[
UserRightsManagerInterface::ROLE_ADMIN,
UserRightsManagerInterface::ROLE_SUEZ,
UserRightsManagerInterface::ROLE_MANAGER,
]
)));
$userId = $user->getId(); // use for validation
$requestId = $request->getId(); // just for logging purposes
$requestContexts = $request->getContexts();
$requestData = $request->getData();
$requestCreatorId = $propertyAccessor->getValue($requestData, '[userId]');
// Logging time
$loggableContexts = array_map(function(UserRequestRequestContext $context) {
return $context->getEnvelope();
}, $requestContexts);
$this->logger->info('[UserRequestVoter] Logged user Id: {userId}', ['userId' => $userId ]);
$this->logger->info('[UserRequestVoter] Request creator id: {requestCreatorId}', ['requestCreatorId' => $requestCreatorId ]);
$this->logger->info('[UserRequestVoter] Request Id: {requestId}', ['requestId' => $requestId ]);
$this->logger->info('[UserRequestVoter] Request contexts: {requestContexts}', [
'requestContexts' => json_encode($loggableContexts)
]);
// Voting time
// Admins can see everything
if ($userIsAdminOrSuezOrManager) {
$this->logger->info('[UserRequestVoter] User is admin');
return true;
}
$this->logger->info('[UserRequestVoter] User is not an admin');
$areaRights = $user->getAreaRights();
$userNarrowestArea = $areaRights->getNarrowestRight();
$this->logger->info('[UserRequestVoter] User narrowest area: {area}', ['area' => json_encode($userNarrowestArea) ]);
// Look if any context ID matches the user's narrowest area
$matchedContext = array_filter($requestContexts, function (UserRequestRequestContext $context) use ($userNarrowestArea) {
return $context->getId() === $userNarrowestArea['id'];
});
$this->logger->info('[UserRequestVoter] matched: {matchedContext}', ['matchedContext' => json_encode($matchedContext) ]);
// If there is one context left, then the request is within the user's area.
if (!empty($matchedContext)) {
$this->logger->info('[UserRequestVoter] Request in user area');
return true;
}
$this->logger->info('[UserRequestVoter] Request not in user area');
// If the user is the creator of the request, they may view it.
if ((string) $userId === (string) $requestCreatorId) {
$this->logger->info('[UserRequestVoter] User created request');
return true;
}
$this->logger->info('[UserRequestVoter] User {id} did not create request (by {creator})', [
'id' => $userId,
'creator' => $requestCreatorId,
]);
// Otherwise, user may not see the request.
return false;
}
}