diff --git a/application/Espo/Core/AclPortal/Base.php b/application/Espo/Core/AclPortal/Base.php index 29b2ee4543..13458ea75d 100644 --- a/application/Espo/Core/AclPortal/Base.php +++ b/application/Espo/Core/AclPortal/Base.php @@ -104,6 +104,13 @@ class Base extends \Espo\Core\Acl\Base } if ($inAccount) { return true; + } else { + if (is_null($isOwnContact) && $entity) { + $isOwnContact = $this->checkIsOwnContact($user, $entity); + } + if ($isOwnContact) { + return true; + } } } @@ -117,7 +124,6 @@ class Base extends \Espo\Core\Acl\Base } return false; - } public function checkReadOnlyAccount(User $user, $data) @@ -152,13 +158,13 @@ class Base extends \Espo\Core\Acl\Base { $accountIdList = $user->getLinkMultipleIdList('accounts'); if (count($accountIdList)) { - if ($entity->hasAttribute('accountId')) { + if ($entity->hasAttribute('accountId') && $entity->getRelationParam('account', 'entity') === 'Account') { if (in_array($entity->get('accountId'), $accountIdList)) { return true; } } - if ($entity->hasRelation('accounts')) { + if ($entity->hasRelation('accounts') && $entity->getRelationParam('accounts', 'entity') === 'Account') { $repository = $this->getEntityManager()->getRepository($entity->getEntityType()); foreach ($accountIdList as $accountId) { if ($repository->isRelated($entity, 'accounts', $accountId)) { @@ -183,13 +189,13 @@ class Base extends \Espo\Core\Acl\Base { $contactId = $user->get('contactId'); if ($contactId) { - if ($entity->hasAttribute('contactId')) { + if ($entity->hasAttribute('contactId') && $entity->getRelationParam('contact', 'entity') === 'Contact') { if ($entity->get('contactId') === $contactId) { return true; } } - if ($entity->hasRelation('contacts')) { + if ($entity->hasRelation('contacts') && $entity->getRelationParam('contacts', 'entity') === 'Contact') { $repository = $this->getEntityManager()->getRepository($entity->getEntityType()); if ($repository->isRelated($entity, 'contacts', $contactId)) { return true; @@ -207,6 +213,4 @@ class Base extends \Espo\Core\Acl\Base return false; } - } - diff --git a/application/Espo/Core/SelectManagers/Base.php b/application/Espo/Core/SelectManagers/Base.php index be9eed8bf0..6455051c27 100644 --- a/application/Espo/Core/SelectManagers/Base.php +++ b/application/Espo/Core/SelectManagers/Base.php @@ -552,16 +552,16 @@ class Base return; } - $d = [ + $or = [ 'teamsAccess.id' => $this->getUser()->getLinkMultipleIdList('teams') ]; if ($this->hasAssignedUserField()) { - $d['assignedUserId'] = $this->getUser()->id; + $or['assignedUserId'] = $this->getUser()->id; } else if ($this->hasCreatedByField()) { - $d['createdById'] = $this->getUser()->id; + $or['createdById'] = $this->getUser()->id; } $result['whereClause'][] = [ - 'OR' => $d + 'OR' => $or ]; } @@ -580,38 +580,42 @@ class Base protected function accessPortalOnlyContact(&$result) { - $d = []; + $or = []; $contactId = $this->getUser()->get('contactId'); if ($contactId) { - if ($this->getSeed()->hasAttribute('contactId')) { - $d['contactId'] = $contactId; + if ( + $this->getSeed()->hasAttribute('contactId') && $this->getSeed()->getRelationParam('contact', 'entity') === 'Contact' + ) { + $or['contactId'] = $contactId; } - if ($this->getSeed()->hasRelation('contacts')) { + if ( + $this->getSeed()->hasRelation('contacts') && $this->getSeed()->getRelationParam('contacts', 'entity') === 'Contact' + ) { $this->addLeftJoin(['contacts', 'contactsAccess'], $result); $this->setDistinct(true, $result); - $d['contactsAccess.id'] = $contactId; + $or['contactsAccess.id'] = $contactId; } } if ($this->getSeed()->hasAttribute('createdById')) { - $d['createdById'] = $this->getUser()->id; + $or['createdById'] = $this->getUser()->id; } if ($this->getSeed()->hasAttribute('parentId') && $this->getSeed()->hasRelation('parent')) { $contactId = $this->getUser()->get('contactId'); if ($contactId) { - $d[] = [ + $or[] = [ 'parentType' => 'Contact', 'parentId' => $contactId ]; } } - if (!empty($d)) { + if (!empty($or)) { $result['whereClause'][] = [ - 'OR' => $d + 'OR' => $or ]; } else { $result['whereClause'][] = [ @@ -622,27 +626,31 @@ class Base protected function accessPortalOnlyAccount(&$result) { - $d = []; + $or = []; $accountIdList = $this->getUser()->getLinkMultipleIdList('accounts'); $contactId = $this->getUser()->get('contactId'); if (count($accountIdList)) { - if ($this->getSeed()->hasAttribute('accountId')) { - $d['accountId'] = $accountIdList; + if ( + $this->getSeed()->hasAttribute('accountId') && $this->getSeed()->getRelationParam('account', 'entity') === 'Account' + ) { + $or['accountId'] = $accountIdList; } - if ($this->getSeed()->hasRelation('accounts')) { + if ( + $this->getSeed()->hasRelation('accounts') && $this->getSeed()->getRelationParam('accounts', 'entity') === 'Account' + ) { $this->addLeftJoin(['accounts', 'accountsAccess'], $result); $this->setDistinct(true, $result); - $d['accountsAccess.id'] = $accountIdList; + $or['accountsAccess.id'] = $accountIdList; } if ($this->getSeed()->hasAttribute('parentId') && $this->getSeed()->hasRelation('parent')) { - $d[] = [ + $or[] = [ 'parentType' => 'Account', 'parentId' => $accountIdList ]; if ($contactId) { - $d[] = [ + $or[] = [ 'parentType' => 'Contact', 'parentId' => $contactId ]; @@ -651,23 +659,27 @@ class Base } if ($contactId) { - if ($this->getSeed()->hasAttribute('contactId')) { - $d['contactId'] = $contactId; + if ( + $this->getSeed()->hasAttribute('contactId') && $this->getSeed()->getRelationParam('contact', 'entity') === 'Contact' + ) { + $or['contactId'] = $contactId; } - if ($this->getSeed()->hasRelation('contacts')) { + if ( + $this->getSeed()->hasRelation('contacts') && $this->getSeed()->getRelationParam('contacts', 'entity') === 'Contact' + ) { $this->addLeftJoin(['contacts', 'contactsAccess'], $result); $this->setDistinct(true, $result); - $d['contactsAccess.id'] = $contactId; + $or['contactsAccess.id'] = $contactId; } } if ($this->getSeed()->hasAttribute('createdById')) { - $d['createdById'] = $this->getUser()->id; + $or['createdById'] = $this->getUser()->id; } - if (!empty($d)) { + if (!empty($or)) { $result['whereClause'][] = [ - 'OR' => $d + 'OR' => $or ]; } else { $result['whereClause'][] = [ diff --git a/tests/integration/Core/BaseTestCase.php b/tests/integration/Core/BaseTestCase.php index 953ddb3ac9..33a181f4ba 100644 --- a/tests/integration/Core/BaseTestCase.php +++ b/tests/integration/Core/BaseTestCase.php @@ -67,9 +67,9 @@ abstract class BaseTestCase extends \PHPUnit\Framework\TestCase protected $initData = null; - protected function createApplication($clearCache = true) + protected function createApplication($clearCache = true, $portalId = null) { - return $this->espoTester->getApplication(true, $clearCache); + return $this->espoTester->getApplication(true, $clearCache, $portalId); } protected function auth($userName, $password = null, $portalId = null, $authenticationMethod = null) diff --git a/tests/integration/Core/Tester.php b/tests/integration/Core/Tester.php index d3d56923b6..46755985ac 100644 --- a/tests/integration/Core/Tester.php +++ b/tests/integration/Core/Tester.php @@ -117,15 +117,18 @@ class Tester $this->authenticationMethod = $authenticationMethod; } - public function getApplication($reload = false, $clearCache = true) + public function getApplication($reload = false, $clearCache = true, $portalId = null) { + $portalId = $portalId ?? $this->portalId ?? null; + if (!isset($this->application) || $reload) { if ($clearCache) { $this->clearCache(); } - $this->application = empty($this->portalId) ? new \Espo\Core\Application() : new \Espo\Core\Portal\Application($this->portalId); + $this->application = !$portalId ? new \Espo\Core\Application() : new \Espo\Core\Portal\Application($portalId); + $auth = new \Espo\Core\Utils\Auth($this->application->getContainer()); if (isset($this->userName)) { diff --git a/tests/integration/Espo/Portal/AclTest.php b/tests/integration/Espo/Portal/AclTest.php new file mode 100644 index 0000000000..fbcdd48c2e --- /dev/null +++ b/tests/integration/Espo/Portal/AclTest.php @@ -0,0 +1,248 @@ +createApplication(); + + $em = $app->getContainer()->get('entityManager'); + + $contact = $em->createEntity('Contact', []); + $portal = $em->createEntity('Portal', [ + 'name' => 'Portal', + ]); + + $this->createUser([ + 'userName' => 'tester', + 'portalsIds' => [$portal->id], + 'contactId' => $contact->id, + ], [ + 'data' => [ + 'Case' => [ + 'create' => 'no', + 'read' => 'contact', + 'edit' => 'no', + 'delete' => 'no', + 'stream' => 'contact', + ] + ], + ], true); + + $this->auth('tester', null, $portal->id); + + $app = $this->createApplication(true, $portal->id); + + $em = $app->getContainer()->get('entityManager'); + + $acl = $app->getContainer()->get('acl'); + + $case1 = $em->createEntity('Case', [ + 'contactId' => $contact->id, + ], ['createdById' => '1']); + $case2 = $em->createEntity('Case', [ + 'contactsIds' => [$contact->id], + ], ['createdById' => '1']); + $case3 = $em->createEntity('Case', [ + ], ['createdById' => '1']); + + $this->assertFalse($acl->check('Case', 'create')); + $this->assertFalse($acl->check('Case', 'edit')); + $this->assertFalse($acl->check('Case', 'delete')); + + $this->assertTrue($acl->check($case1, 'read')); + $this->assertTrue($acl->check($case2, 'read')); + $this->assertFalse($acl->check($case3, 'read')); + + $service = $app->getContainer()->get('serviceFactory')->create('Case'); + $result = $service->find([]); + + $idList = []; + foreach ($result['collection'] as $e) { + $idList[] = $e->id; + } + + $this->assertTrue(in_array($case1->id, $idList)); + $this->assertTrue(in_array($case2->id, $idList)); + $this->assertFalse(in_array($case3->id, $idList)); + } + + public function testAccessAccount() + { + $app = $this->createApplication(); + + $em = $app->getContainer()->get('entityManager'); + + $contact = $em->createEntity('Contact', []); + $account = $em->createEntity('Account', []); + $portal = $em->createEntity('Portal', [ + 'name' => 'Portal', + ]); + + $this->createUser([ + 'userName' => 'tester', + 'portalsIds' => [$portal->id], + 'contactId' => $contact->id, + 'accountsIds' => [$account->id], + ], [ + 'data' => [ + 'Case' => [ + 'create' => 'no', + 'read' => 'account', + 'edit' => 'no', + 'delete' => 'no', + 'stream' => 'account', + ] + ], + ], true); + + $this->auth('tester', null, $portal->id); + + $app = $this->createApplication(true, $portal->id); + + $em = $app->getContainer()->get('entityManager'); + + $acl = $app->getContainer()->get('acl'); + + $case1 = $em->createEntity('Case', [ + 'contactId' => $contact->id, + ], ['createdById' => '1']); + $case2 = $em->createEntity('Case', [ + 'contactsIds' => [$contact->id], + ], ['createdById' => '1']); + $case3 = $em->createEntity('Case', [ + ], ['createdById' => '1']); + $case4 = $em->createEntity('Case', [ + 'accountId' => $account->id, + ], ['createdById' => '1']); + + $this->assertFalse($acl->check('Case', 'create')); + $this->assertFalse($acl->check('Case', 'edit')); + $this->assertFalse($acl->check('Case', 'delete')); + + $this->assertTrue($acl->check($case1, 'read')); + $this->assertTrue($acl->check($case2, 'read')); + $this->assertFalse($acl->check($case3, 'read')); + $this->assertTrue($acl->check($case4, 'read')); + + $service = $app->getContainer()->get('serviceFactory')->create('Case'); + $result = $service->find([]); + + $idList = []; + foreach ($result['collection'] as $e) { + $idList[] = $e->id; + } + + $this->assertTrue(in_array($case1->id, $idList)); + $this->assertTrue(in_array($case2->id, $idList)); + $this->assertFalse(in_array($case3->id, $idList)); + $this->assertTrue(in_array($case4->id, $idList)); + } + + public function testAccessOwn() + { + $app = $this->createApplication(); + + $em = $app->getContainer()->get('entityManager'); + + $contact = $em->createEntity('Contact', []); + $account = $em->createEntity('Account', []); + $portal = $em->createEntity('Portal', [ + 'name' => 'Portal', + ]); + + $this->createUser([ + 'userName' => 'tester', + 'portalsIds' => [$portal->id], + 'contactId' => $contact->id, + 'accountsIds' => [$account->id], + ], [ + 'data' => [ + 'Case' => [ + 'create' => 'no', + 'read' => 'own', + 'edit' => 'no', + 'delete' => 'no', + 'stream' => 'own', + ] + ], + ], true); + + $this->auth('tester', null, $portal->id); + + $app = $this->createApplication(true, $portal->id); + + $em = $app->getContainer()->get('entityManager'); + + $acl = $app->getContainer()->get('acl'); + $user = $app->getContainer()->get('user'); + + $case1 = $em->createEntity('Case', [ + 'contactId' => $contact->id, + ], ['createdById' => '1']); + $case2 = $em->createEntity('Case', [ + 'contactsIds' => [$contact->id], + ], ['createdById' => '1']); + $case3 = $em->createEntity('Case', [ + ], ['createdById' => '1']); + $case4 = $em->createEntity('Case', [ + 'accountId' => $account->id, + ], ['createdById' => '1']); + $case5 = $em->createEntity('Case', [ + 'accountId' => $account->id, + ], ['createdById' => $user->id]); + + $this->assertFalse($acl->check('Case', 'create')); + $this->assertFalse($acl->check('Case', 'edit')); + $this->assertFalse($acl->check('Case', 'delete')); + + $this->assertFalse($acl->check($case1, 'read')); + $this->assertFalse($acl->check($case2, 'read')); + $this->assertFalse($acl->check($case3, 'read')); + $this->assertFalse($acl->check($case4, 'read')); + $this->assertTrue($acl->check($case5, 'read')); + + $service = $app->getContainer()->get('serviceFactory')->create('Case'); + $result = $service->find([]); + + $idList = []; + foreach ($result['collection'] as $e) { + $idList[] = $e->id; + } + + $this->assertFalse(in_array($case1->id, $idList)); + $this->assertFalse(in_array($case2->id, $idList)); + $this->assertFalse(in_array($case3->id, $idList)); + $this->assertFalse(in_array($case4->id, $idList)); + $this->assertTrue(in_array($case5->id, $idList)); + } +}