<?php
/*
* Este arquivo é parte do package Login Bundle.
*
* (c) Laino Santos <lainosantos@recife.pe.gov.br>
*
* Para informações completas sobre copyright e licença, por favor ver o
* arquivo LICENCE distribuído juntamente com este código.
*/
namespace Emprel\Login\Bundle\Security\Voter;
use Emprel\OpenIdUmaClient\Exceptions\NotAuthorizedException;
use Emprel\OpenIdUmaClient\Uma\Representation\ResourceRepresentation;
use Emprel\Login\Bundle\Security\User\LoginUser;
use Emprel\Login\Bundle\Security\Voter\Exception\ResourceNotFoundException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
class ResourceVoter implements VoterInterface
{
/**
* @var array
*/
protected static $cache = [];
public function vote(TokenInterface $token, $subject, array $attributes)
{
if (!$token->getUser() instanceof LoginUser || !$subject instanceof ResourceVoterSubject) {
return self::ACCESS_ABSTAIN;
} elseif (($resource = $this->getResource($token, $subject)) === null) {
return self::ACCESS_DENIED;
} elseif (!$this->supports($token, $subject, $attributes, $resource)) {
return self::ACCESS_ABSTAIN;
}
if ($this->voteOnResource($token, $subject, $attributes, $resource)) {
return self::ACCESS_GRANTED;
} else {
return self::ACCESS_DENIED;
}
}
protected function getResource(TokenInterface $token, ResourceVoterSubject $subject): ResourceRepresentation
{
$cacheKey = md5($subject->getId() . ':' . $subject->getLocalId() . ':' . $subject->getUri() . ':' . $subject->getType());
if (!$subject->isAllowCache() || !isset(static::$cache[$cacheKey])) {
$resources = $subject->getUmaClient()->getResources(
true, $subject->getId(), $subject->getLocalId(), $subject->getUri(), null, $subject->getType()
);
static::$cache[$cacheKey] = $resources;
} else {
$resources = static::$cache[$cacheKey];
}
if (count($resources) !== 1) {
throw new ResourceNotFoundException();
}
return $resources[0];
}
protected function supports(TokenInterface $token, ResourceVoterSubject $subject, $scopes, ResourceRepresentation $resource): bool
{
return true;
}
protected function voteOnResource(TokenInterface $token, ResourceVoterSubject $subject, $scopes, ResourceRepresentation $resource): bool
{
$context = $subject->getPolicyContext();
array_multisort($context);
array_multisort($scopes);
$cacheKey = md5(serialize($context));
try {
if (!$subject->isAllowCache() || !isset(static::$cache[$cacheKey])) {
$permissions = $subject->getUmaClient()->getPermissions($token->getUser()->getTokenWrapper()->getAccessToken(), $context);
static::$cache[$cacheKey] = $permissions;
} else {
$permissions = static::$cache[$cacheKey];
}
} catch (NotAuthorizedException $exception) {
static::$cache[$cacheKey] = false;
return false;
}
if ($permissions === false) {
return false;
}
foreach ($permissions as $permission) {
if ($permission->resourceId === $resource->id) {
foreach ($scopes as $scope) {
if ($scope !== null && in_array($scope, $permission->scopes)) {
return true;
}
}
// Decisão unânime faz com que as chamadas com atributos vazios não chegem ao voter, por isso passar null
return empty($scopes) || (count($scopes) === 1 && $scopes[0] === null);
}
}
return false;
}
public static function clearCache(): void {
static::$cache = [];
}
}