Hello and thanks for your help and time in advance. I’m currently building an API where people can upload Jobs
of any kind. These Jobs all have an APIKey
assigned to them. Jobs
also do have a type
, which should also be restricted. An APIKey
contains all the allowed types
for which the APIKey
was created.
In summary:
- Create an EntityPrivilege to prevent other people fetching Jobs which were not created with their APIKey
- Restrict creation of
Jobs
withtypes
, which are not allowed for the specificAPIKey
The Problem:
Fetching of other entities not created with an APIKey is still possible (Added extra privilege not being used because of suggestions => now returns empty array)
Creation of types which are not allowed is also still possible with now restrictions being made.
For this, following Codes has been written.
Policy.yaml
privilegeTargets:
'Neos\Flow\Security\Authorization\Privilege\Method\MethodPrivilege':
'Webco.AIApi:ApiUploadEndpoint':
matcher: 'method(Webco\AIApi\Controller\JobController->(bulk|get|delete)Action())'
'Webco.AIApi:ApiUploadEndpoint.allowedAI':
matcher: 'method(Webco\AIApi\Controller\JobController->createAction(job.type in current.ai.allowedAIs))'
'Neos\Flow\Security\Authorization\Privilege\Entity\Doctrine\EntityPrivilege':
'Webco.AIApi:JobEntity':
matcher: 'isType("Webco\AIApi\Domain\Model\Job") && property("apiKey").equals("current.apikey.key")'
'Webco.AIApi:JobEntityExtra':
matcher: 'isType("Webco\AIApi\Domain\Model\Job")'
roles:
'Webco.AIApi:ApiKey':
privileges:
- privilegeTarget: 'Webco.AIApi:ApiUploadEndpoint'
permission: GRANT
- privilegeTarget: 'Webco.AIApi:ApiUploadEndpoint.allowedAI'
permission: GRANT
- privilegeTarget: 'Webco.AIApi:JobEntity'
permission: GRANT
Also there are contexts being used:
AIContext (Type):
final class AIContext implements CacheAwareInterface
{
/**
* @Flow\Inject
* @var Context
*/
protected $securityContext;
/**
* @Flow\Inject
* @var PsrSystemLoggerInterface
*/
protected $systemLogger;
/**
* @Flow\Inject
* @var APIKeyRepository
*/
protected $apiKeyRepository;
public function getAllowedAIs(): array
{
/** @var APIKey $apiKey */
$apiKey = $this->apiKeyRepository->findByAccount($this->securityContext->getAccount());
return $apiKey->getAllowedAIs();
}
public function getCacheEntryIdentifier(): string
{
$ais = $this->getAllowedAIs();
if (!$ais) {
return sha1("ENOAIS");
//throw new Exception('Customer not available', 1597409719);
}
$names = [];
foreach ($ais as $ai) {
$names[] = $ai->getName();
}
return sha1(implode(";", $names));
}
}
and APIKeyContext:
/**
* @Flow\Scope("singleton")
*/
class APIKeyContext implements CacheAwareInterface
{
/**
* @Flow\Inject
* @var LoggerInterface
*/
protected $systemLogger;
/**
* @Flow\Inject
* @var Context
*/
protected $securityContext;
/**
* @Flow\Inject
* @var BrokerRepository
*/
protected $brokerRepository;
/**
* @Flow\Inject
* @var APIKeyRepository
*/
protected $apiKeyRepository;
/**
* @Flow\Inject
* @var PersistenceManagerInterface
*/
protected $persistenceManager;
/**
* @Flow\Inject
* @var PolicyService
*/
protected $policyService;
/**
* @return ?Customer
*/
public function getKey(): ?APIKey
{
$account = $this->securityContext->getAccount();
if ($account) {
$name = $account->getAccountIdentifier();
switch (true) {
case $account->hasRole($this->policyService->getRole('Webco.AIApi:ApiKey')):
/** @var APIKey $apiKey */
$apiKey = $this->apiKeyRepository->findOneByAccount($account);
$key = $apiKey->getPublicKey();
$this->systemLogger->debug(__CLASS__ . " APIKEY: $key");
return $apiKey;
break;
case $account->hasRole($this->policyService->getRole('Webco.AIApi:Broker')):
// Broker has no customer associated
/** @var Broker $broker */
//$broker = $this->brokerRepository->findByAccount($account);
break;
default:
// user account
break;
}
}
}
/**
* @return string
*/
public function getCacheEntryIdentifier(): string
{
$apiKey = $this->getKey();
if (!$apiKey) {
return sha1("ENOAPIKEY");
//throw new Exception('Customer not available', 1597409719);
}
return $this->persistenceManager->getIdentifierByObject($apiKey);
}
}
Output has been correctly identified with systemLogger for both contexts.
My createAction() contains a Model Job which has the property “type” (string) mapped from the request body to an “type” (AI Model).