<?php
namespace App\Voter;
use App\Exception\AppException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class AccessVoter extends Voter
{
public const UPLOAD = 'upload';
public const STREAM = 'stream';
public const INTERVIEW = 'interview';
public const INTERVIEW_LIST = 'interview-list';
private const ATTRIBUTES = [
self::UPLOAD,
self::STREAM,
self::INTERVIEW,
self::INTERVIEW_LIST,
];
private HttpClientInterface $client;
private LoggerInterface $logger;
public function __construct(HttpClientInterface $client, LoggerInterface $logger)
{
$this->client = $client;
$this->logger = $logger;
}
protected function supports(string $attribute, $subject): bool
{
return $subject instanceof Request && in_array($attribute, self::ATTRIBUTES);
}
protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool
{
switch ($attribute) {
case self::UPLOAD:
return $this->isUploadAllowed($subject);
case self::STREAM:
return $this->isStreamAllowed($subject);
case self::INTERVIEW:
return $this->isInterviewAllowed($subject);
case self::INTERVIEW_LIST:
return $this->isInterviewListAllowed($subject);
}
throw new AppException('Invalid attribute: '.$attribute);
}
private function isUploadAllowed(Request $request): bool
{
if ((null === $request->headers->get('apptoken')) || !$this->isValidApplication($request->headers->get('apptoken'))) {
throw new AppException('Access restricted.', 403);
}
return true;
}
private function isStreamAllowed(Request $request): bool
{
$content = json_decode($request->getContent(), true);
if ($content['appKey'] !== $_ENV['APP_KEY']) {
return false;
}
if (!isset($content['custom']['appToken']) || !$this->isValidApplication($content['custom']['appToken'])) {
return false;
}
return true;
}
private function isValidApplication($appToken): bool
{
/** dev environment token check */
if ($appToken === 'T-49d2016b-e492-4851-b9b4-d103244a2fdd') {
return true;
}
$response = $this->client->request(
'GET',
$_ENV['API_URL'] . '/api/v1/_self',
[
'headers' => [
'SpxAuth' => 'Bearer ' . $appToken,
]
]
);
if ($response->getStatusCode() < 300) {
return true;
}
$response = $this->client->request(
'GET',
$_ENV['API_URL'] . '/api/v1/video_questions/allow_recording/' . $appToken
);
if ($response->getStatusCode() < 300) {
return true;
}
$response = $this->client->request(
'GET',
$_ENV['API_URL'] . '/api/v1/video_questions/allow_update_universal_job/' . $appToken
);
return $response->getStatusCode() < 300;
}
private function isInterviewAllowed(Request $request)
{
$content = json_decode($request->getContent(), true);
$this->logger->info(json_encode($content));
$customerToken = $content['custom']['token'];
$response = $this->client->request(
'GET',
$_ENV['API_URL'] . '/api/v1/_self',
[
'headers' => [
'SpxAuth' => 'Bearer ' . $customerToken,
]
]
);
return $response->getStatusCode() < 300;
}
private function isInterviewListAllowed(Request $request)
{
$appToken = $request->headers->get('Apptoken');
if (empty($appToken)) {
$appToken = $request->headers->get('SpxAuth');
if (empty($appToken)) {
throw new AppException('Missing authorization', 403);
}
$appToken = str_replace('Bearer ', '', $appToken);
}
$response = $this->client->request(
'GET',
$_ENV['API_URL'] . '/api/v1/_self',
[
'headers' => [
'SpxAuth' => 'Bearer ' . $appToken,
]
]
);
return $response->getStatusCode() < 300;
}
}