In Espo we can set ACL for entities with default choices such as 'All', 'account', 'contact', 'own','no'.
Suppose we have some custom entity `MyCustomEntity`
`MyCustomEntity` has Many-To-One `Account` relation
We can set ACL for 'MyCustomEntity` to `Account` for read,write, etc in GUI and if user visits http://{espoUrl}#MyCustomEntity they can only see/access MyCustomEntities related to their `Account` as expected.
Suppose `MyCustomEntity` has One-To-Many `MyCustomEntityChild` relation
We can set ACL for `MyCustomEntityChild` to 'account' for read, write, etc. and if user visits http://{espoUrl}#MyCustomEntityChild they see nothing unless `MyCustomEntityChild` has relation defined to `Account` as well.
So my question is, what is the best practice here? It seems like having to have a duplicate relationship to `Account` and have extra database field `accountId` along with saveFormula or saveHook to set accountId/accountName from parent when creating `MyCustomEntityChild` is redundant. Or is it better to define custom AccessChecker classes for such grandchild relations in order to avoid having to duplicate Account relationships?
In short, `MyCustomEntityChild` is related to `MyCustomEntity` which is related to `Account` so what is the recommended way to ensure account user can only see/access MyCustomEntityChild related to their account.
Options:
1. Create `MyCustomEntityChild` Many-To-One `Account` relation and implement saveFormula or saveHook to set accountId, accountName based on `MyCustomEntity` accountId and accountName.
2. Create custom AccessChecker class for `MyCustomEntityChild` and do something like below.
3. Other suggestions?
PHP Code:
<?php
namespace Espo\Custom\Classes\Acl\MyCustomEntityChild;
use Espo\Classes\Acl\Attachment\AccessChecker as AttachmentAccessChecker;
use Espo\Core\Acl\Traits\DefaultAccessCheckerDependenc y;
use Espo\Core\Acl\AccessEntityCREDChecker;
use Espo\Core\Acl\DefaultAccessChecker;
use Espo\ORM\Entity;
use Espo\Entities\User;
use Espo\Core\Acl\ScopeData;
use Espo\ORM\EntityManager;
class TemplateAccessChecker implements AccessEntityCREDChecker
{
use DefaultAccessCheckerDependency;
private AttachmentAccessChecker $parentAccessChecker;
private EntityManager $entityManager;
public function __construct(
AttachmentAccessChecker $parentAccessChecker,
DefaultAccessChecker $defaultAccessChecker,
EntityManager $entityManager,
// here add needed additional dependencies
)
{
$this->parentAccessChecker = $parentAccessChecker;
$this->defaultAccessChecker = $defaultAccessChecker;
$this->entityManager = $entityManager;
}
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
// here add your custom check that returns true if some conditions met
$userAccountIds = $user?->getAccounts()->getIdList();
$myCustomEntityAccountId = $this->entityManager
->getRDBRepository($entity->getEntityType())
->getRelation($entity, 'MyCustomEntity')
->findOne()?->get('accountId');
if( in_array($myCustomEntityAccountId, $userAccountIds) ) {return true;}
return $this->parentAccessChecker->checkEntityRead($user, $entity, $data);
}
}
Thank you in advance.