acl refactoring

This commit is contained in:
Yuri Kuznetsov
2021-04-04 13:17:39 +03:00
parent e13ca70143
commit 5d97a16d75
17 changed files with 583 additions and 191 deletions
-2
View File
@@ -42,8 +42,6 @@ use Espo\Core\{
class Email extends Acl implements EntityReadAcl
{
protected $ownerUserIdAttribute = 'usersIds';
public function checkEntityRead(EntityUser $user, Entity $entity, ScopeData $data): bool
{
if ($this->checkEntity($user, $entity, $data, 'read')) {
-2
View File
@@ -42,8 +42,6 @@ use Espo\Core\{
class Email extends Acl implements EntityReadAcl
{
protected $ownerUserIdAttribute = 'usersIds';
public function checkEntityRead(EntityUser $user, Entity $entity, ScopeData $data): bool
{
if ($this->checkEntity($user, $entity, $data, Table::ACTION_READ)) {
-32
View File
@@ -288,38 +288,6 @@ class Acl implements ScopeAcl, EntityAcl, EntityDeleteAcl
return true;
}
public function getOwnerUserIdAttribute(): ?string
{
if ($this->ownerUserIdAttribute) {
return $this->ownerUserIdAttribute;
}
if (!$this->scope) {
return null;
}
$defs = $this->entityManager->getDefs()->getEntity($this->scope);
if (
$defs->hasField(self::FIELD_ASSIGNED_USERS) &&
$defs->getField(self::FIELD_ASSIGNED_USERS)->getType() === 'linkMultiple' &&
$defs->hasRelation(self::FIELD_ASSIGNED_USERS) &&
$defs->getRelation(self::FIELD_ASSIGNED_USERS)->getForeignEntityType() === 'User'
) {
return self::ATTR_ASSIGNED_USERS_IDS;
}
if ($defs->hasAttribute(self::ATTR_ASSIGNED_USER_ID)) {
return self::ATTR_ASSIGNED_USER_ID;
}
if ($defs->hasAttribute(self::ATTR_CREATED_BY_ID)) {
return self::ATTR_CREATED_BY_ID;
}
return null;
}
/**
* @deprecated Use `$this->config`.
*/
+1 -7
View File
@@ -33,7 +33,7 @@ use Espo\ORM\Entity;
use Espo\Entities\User;
interface EntityAcl
interface EntityAcl extends ScopeAcl
{
/**
* Check access to an entity.
@@ -44,10 +44,4 @@ interface EntityAcl
* Get an access level for a specific action.
*/
public function getLevel(User $user, ScopeData $data, string $action) : string;
/**
* Get an entity attribute that stores an ID (or IDs) of an owner-user.
* NULL means no owner.
*/
public function getOwnerUserIdAttribute(): ?string;
}
@@ -0,0 +1,93 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace Espo\Core\Acl;
use Espo\Core\Utils\Metadata;
use Espo\ORM\Defs\Defs;
class OwnerUserFieldProvider
{
protected const FIELD_ASSIGNED_USERS = 'assignedUsers';
protected const FIELD_ASSIGNED_USER = 'assignedUser';
protected const FIELD_CREATED_BY = 'createdBy';
private $ormDefs;
private $metadata;
public function __construct(Defs $ormDefs, Metadata $metadata)
{
$this->ormDefs = $ormDefs;
$this->metadata = $metadata;
}
public function get(string $entityType) : ?string
{
$value = $this->metadata->get(['entityAcl', $entityType, 'readOwnerUserField']);
if ($value) {
return $value;
}
$defs = $this->ormDefs->getEntity($entityType);
if (
$defs->hasField(self::FIELD_ASSIGNED_USERS) &&
$defs->getField(self::FIELD_ASSIGNED_USERS)->getType() === 'linkMultiple' &&
$defs->hasRelation(self::FIELD_ASSIGNED_USERS) &&
$defs->getRelation(self::FIELD_ASSIGNED_USERS)->getForeignEntityType() === 'User'
) {
return self::FIELD_ASSIGNED_USERS;
}
if (
$defs->hasField(self::FIELD_ASSIGNED_USER) &&
$defs->getField(self::FIELD_ASSIGNED_USER)->getType() === 'link' &&
$defs->hasRelation(self::FIELD_ASSIGNED_USER) &&
$defs->getRelation(self::FIELD_ASSIGNED_USER)->getForeignEntityType() === 'User'
) {
return self::FIELD_ASSIGNED_USER;
}
if (
$defs->hasField(self::FIELD_CREATED_BY) &&
$defs->getField(self::FIELD_CREATED_BY)->getType() === 'link' &&
$defs->hasRelation(self::FIELD_CREATED_BY) &&
$defs->getRelation(self::FIELD_CREATED_BY)->getForeignEntityType() === 'User'
) {
return self::FIELD_CREATED_BY;
}
return null;
}
}
+15 -1
View File
@@ -38,6 +38,7 @@ use Espo\Core\{
Acl\AclFactory,
Acl\GlobalRestrictonFactory,
Acl\GlobalRestricton,
Acl\OwnerUserFieldProvider,
Acl as UserAclWrapper,
Acl\Table as Table,
Acl\ScopeAcl,
@@ -83,15 +84,19 @@ class AclManager
protected $globalRestricton;
protected $ownerUserFieldProvider;
public function __construct(
InjectableFactory $injectableFactory,
EntityManager $entityManager,
AclFactory $aclFactory,
GlobalRestrictonFactory $globalRestrictonFactory
GlobalRestrictonFactory $globalRestrictonFactory,
OwnerUserFieldProvider $ownerUserFieldProvider
) {
$this->injectableFactory = $injectableFactory;
$this->entityManager = $entityManager;
$this->aclFactory = $aclFactory;
$this->ownerUserFieldProvider = $ownerUserFieldProvider;
$this->globalRestricton = $globalRestrictonFactory->create();
}
@@ -635,4 +640,13 @@ class AclManager
return $this->globalRestricton->getScopeRestrictedLinkList($scope, $type);
}
/**
* Get an entity field that stores an owner-user (or multiple users).
* Must be link or linkMulitple field. NULL means no owner.
*/
public function getReadOwnerUserField(string $entityType): ?string
{
return $this->ownerUserFieldProvider->get($entityType);
}
}
@@ -42,6 +42,7 @@ use Espo\Core\{
AclPortal\AclFactory as PortalAclFactory,
Acl\ScopeAcl,
Acl\GlobalRestrictonFactory,
Acl\OwnerUserFieldProvider,
Acl\Table as TableBase,
Portal\Acl as UserAclWrapper,
AclManager as BaseAclManager,
@@ -69,11 +70,13 @@ class AclManager extends BaseAclManager
EntityManager $entityManager,
PortalAclFactory $aclFactory,
GlobalRestrictonFactory $globalRestrictonFactory,
OwnerUserFieldProvider $ownerUserFieldProvider,
BaseAclManager $mainManager
) {
$this->injectableFactory = $injectableFactory;
$this->entityManager = $entityManager;
$this->aclFactory = $aclFactory;
$this->ownerUserFieldProvider = $ownerUserFieldProvider;
$this->mainManager = $mainManager;
$this->globalRestricton = $globalRestrictonFactory->create();
@@ -50,11 +50,21 @@ class StreamNotesAcl
public function afterSave(Entity $entity, array $options = [])
{
if (!empty($options['noStream'])) return;
if (!empty($options['silent'])) return;
if (!empty($options['skipStreamNotesAcl'])) return;
if (!empty($options['noStream'])) {
return;
}
if ($entity->isNew()) return;
if (!empty($options['silent'])) {
return;
}
if (!empty($options['skipStreamNotesAcl'])) {
return;
}
if ($entity->isNew()) {
return;
}
if (!$this->noteService) {
$this->noteService = $this->serviceFactory->create('Note');
+129 -42
View File
@@ -40,6 +40,8 @@ use Espo\Core\{
use Espo\Entities\User;
use StdClass;
class Notifications
{
protected $notificationService = null;
@@ -49,9 +51,13 @@ class Notifications
public static $order = 14;
protected $metadata;
protected $entityManager;
protected $serviceFactory;
protected $user;
protected $internalAclManager;
public function __construct(
@@ -72,12 +78,15 @@ class Notifications
{
$mentionedUserList = [];
$data = $entity->get('data');
if (($data instanceof \StdClass) && ($data->mentions instanceof \StdClass)) {
if (($data instanceof StdClass) && ($data->mentions instanceof StdClass)) {
$mentions = get_object_vars($data->mentions);
foreach ($mentions as $d) {
$mentionedUserList[] = $d->id;
}
}
return $mentionedUserList;
}
@@ -100,16 +109,27 @@ class Notifications
$notifyUserIdList = [];
if ($parentType && $parentId) {
$userList = $this->getSubscriberList($parentType, $parentId, $entity->get('isInternal'));
$userList = $this->getSubscriberList($parentType, $parentId, $entity->get('isInternal'));
$userIdMetList = [];
foreach ($userList as $user) {
$userIdMetList[] = $user->id;
}
if ($superParentType && $superParentId) {
$additionalUserList = $this->getSubscriberList($superParentType, $superParentId, $entity->get('isInternal'));
$additionalUserList = $this
->getSubscriberList($superParentType, $superParentId, $entity->get('isInternal'));
foreach ($additionalUserList as $user) {
if ($user->isPortal()) continue;
if (in_array($user->id, $userIdMetList)) continue;
if ($user->isPortal()) {
continue;
}
if (in_array($user->id, $userIdMetList)) {
continue;
}
$userIdMetList[] = $user->id;
$userList[] = $user;
}
@@ -117,14 +137,17 @@ class Notifications
if ($entity->get('relatedType')) {
$targetType = $entity->get('relatedType');
} else {
}
else {
$targetType = $parentType;
}
$skipAclCheck = false;
if (!$entity->isAclProcessed()) {
$skipAclCheck = true;
} else {
}
else {
$teamIdList = $entity->getLinkMultipleIdList('teams');
$userIdList = $entity->getLinkMultipleIdList('users');
}
@@ -132,19 +155,24 @@ class Notifications
foreach ($userList as $user) {
if ($skipAclCheck) {
$notifyUserIdList[] = $user->id;
continue;
}
if ($user->isAdmin()) {
$notifyUserIdList[] = $user->id;
continue;
}
if ($user->isPortal()) {
if ($entity->get('relatedType')) {
continue;
} else {
}
else {
$notifyUserIdList[] = $user->id;
}
continue;
}
@@ -152,26 +180,34 @@ class Notifications
if ($level === 'all') {
$notifyUserIdList[] = $user->id;
continue;
} else if ($level === 'team') {
}
else if ($level === 'team') {
if (in_array($user->id, $userIdList)) {
$notifyUserIdList[] = $user->id;
continue;
}
if (!empty($teamIdList)) {
$userTeamIdList = $user->getLinkMultipleIdList('teams');
foreach ($teamIdList as $teamId) {
if (in_array($teamId, $userTeamIdList)) {
$notifyUserIdList[] = $user->id;
break;
}
}
}
continue;
} else if ($level === 'own') {
if (in_array($user->id, $userIdList)) {
$notifyUserIdList[] = $user->id;
continue;
}
}
@@ -179,60 +215,104 @@ class Notifications
} else {
$targetType = $entity->get('targetType');
if ($targetType === 'users') {
$targetUserIdList = $entity->get('usersIds');
if (is_array($targetUserIdList)) {
foreach ($targetUserIdList as $userId) {
if ($userId === $this->user->id) continue;
if (in_array($userId, $notifyUserIdList)) continue;
if ($userId === $this->user->id) {
continue;
}
if (in_array($userId, $notifyUserIdList)) {
continue;
}
$notifyUserIdList[] = $userId;
}
}
} else if ($targetType === 'teams') {
}
else if ($targetType === 'teams') {
$targetTeamIdList = $entity->get('teamsIds');
if (is_array($targetTeamIdList)) {
foreach ($targetTeamIdList as $teamId) {
$team = $this->entityManager->getEntity('Team', $teamId);
if (!$team) continue;
$targetUserList = $this->entityManager->getRepository('Team')->findRelated($team, 'users', array(
'whereClause' => array(
'isActive' => true
)
));
if (!$team) {
continue;
}
$targetUserList = $this->entityManager
->getRDBRepository('Team')
->getRelation($team, 'users')
->where([
'isActive' => true,
])
->find();
foreach ($targetUserList as $user) {
if ($user->id === $this->user->id) continue;
if (in_array($user->id, $notifyUserIdList)) continue;
if ($user->id === $this->user->id) {
continue;
}
if (in_array($user->id, $notifyUserIdList)) {
continue;
}
$notifyUserIdList[] = $user->id;
}
}
}
} else if ($targetType === 'portals') {
}
else if ($targetType === 'portals') {
$targetPortalIdList = $entity->get('portalsIds');
if (is_array($targetPortalIdList)) {
foreach ($targetPortalIdList as $portalId) {
$portal = $this->entityManager->getEntity('Portal', $portalId);
if (!$portal) continue;
$targetUserList = $this->entityManager->getRepository('Portal')->findRelated($portal, 'users', array(
'whereClause' => array(
'isActive' => true
)
));
if (!$portal) {
continue;
}
$targetUserList = $this->entityManager
->getRDBRepository('Portal')
->getRelation($portal, 'users')
->where([
'isActive' => true,
])
->find();
foreach ($targetUserList as $user) {
if ($user->id === $this->user->id) continue;
if (in_array($user->id, $notifyUserIdList)) continue;
if ($user->id === $this->user->id) {
continue;
}
if (in_array($user->id, $notifyUserIdList)) {
continue;
}
$notifyUserIdList[] = $user->id;
}
}
}
} else if ($targetType === 'all') {
$targetUserList = $this->entityManager->getRepository('User')->find([
'whereClause' => [
}
else if ($targetType === 'all') {
$targetUserList = $this->entityManager
->getRepository('User')
->where([
'isActive' => true,
'type' => ['regular', 'admin']
]
]);
'type' => ['regular', 'admin'],
])
->find();
foreach ($targetUserList as $user) {
if ($user->id === $this->user->id) continue;
if ($user->id === $this->user->id) {
continue;
}
$notifyUserIdList[] = $user->id;
}
}
@@ -243,18 +323,25 @@ class Notifications
foreach ($notifyUserIdList as $i => $userId) {
if ($entity->isUserIdNotified($userId)) {
unset($notifyUserIdList[$i]);
continue;
}
if (!$entity->isNew()) {
if (
$this->entityManager->getRepository('Notification')->select(['id'])->where([
'type' => 'Note',
'relatedType' => 'Note',
'relatedId' => $entity->id,
'userId' => $userId,
])->findOne()
$this->entityManager
->getRepository('Notification')
->select(['id'])
->where([
'type' => 'Note',
'relatedType' => 'Note',
'relatedId' => $entity->id,
'userId' => $userId,
])
->findOne()
) {
unset($notifyUserIdList[$i]);
continue;
}
}
@@ -42,8 +42,6 @@ use Espo\Core\{
class Meeting extends Acl implements EntityReadAcl
{
protected $ownerUserIdAttribute = 'usersIds';
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
{
if ($this->checkEntity($user, $entity, $data, Table::ACTION_READ)) {
@@ -0,0 +1,3 @@
{
"readOwnerUserField": "users"
}
@@ -0,0 +1,3 @@
{
"readOwnerUserField": "users"
}
@@ -8,5 +8,6 @@
"users": {
"onlyAdmin": true
}
}
},
"readOwnerUserField": "users"
}
+118 -75
View File
@@ -41,8 +41,8 @@ use Espo\Entities\Note as NoteEntity;
use Espo\ORM\Entity;
use DateTime;
use Exception;
use StdClass;
use Exception;
class Note extends Record
{
@@ -104,9 +104,9 @@ class Note extends Record
$parentId = $data->parentId ?? null;
if ($parentType && $parentId) {
$entity = $this->entityManager->getEntity($data->parentType, $data->parentId);
$parent = $this->entityManager->getEntity($data->parentType, $data->parentId);
if ($entity && !$this->acl->check($entity, AclTable::ACTION_READ)) {
if ($parent && !$this->acl->check($parent, AclTable::ACTION_READ)) {
throw new Forbidden();
}
}
@@ -372,6 +372,9 @@ class Note extends Record
}
}
/**
* Changes users and teams of notes related to an entity according users and teams of the entity.
*/
public function processNoteAcl(Entity $entity, bool $forceProcessNoteNotifications = false): void
{
$entityType = $entity->getEntityType();
@@ -388,33 +391,45 @@ class Note extends Record
return;
}
$ownerUserIdAttribute = $this->getAclManager()
->getImplementation($entityType)
->getOwnerUserIdAttribute();
$usersAttributeIsChanged = false;
$teamsAttributeIsChanged = false;
if ($ownerUserIdAttribute) {
$ownerUserField = $this->getAclManager()->getReadOwnerUserField($entityType);
/* @var $defs \Espo\ORM\Defs\EntityDefs */
$defs = $this->entityManager->getDefs()->getEntity($entity->getEntityType());
$userIdList = [];
if ($ownerUserField) {
if (!$defs->hasField($ownerUserField)) {
throw new Error("Non-existing read-owner user field.");
}
$fieldDefs = $defs->getField($ownerUserField);
if ($fieldDefs->getType() === 'linkMultiple') {
$ownerUserIdAttribute = $ownerUserField . 'Ids';
}
else if ($fieldDefs->getType() === 'link') {
$ownerUserIdAttribute = $ownerUserField . 'Id';
}
else {
throw new Error("Bad read-owner user field type.");
}
if ($entity->isAttributeChanged($ownerUserIdAttribute)) {
$usersAttributeIsChanged = true;
}
if ($usersAttributeIsChanged || $forceProcessNoteNotifications) {
if ($entity->getAttributeParam($ownerUserIdAttribute, 'isLinkMultipleIdList')) {
$userLink = $entity->getAttributeParam($ownerUserIdAttribute, 'relation');
$userIdList = $entity->getLinkMultipleIdList($userLink);
if ($fieldDefs->getType() === 'linkMultiple') {
$userIdList = $entity->getLinkMultipleIdList($ownerUserField);
}
else {
$userId = $entity->get($ownerUserIdAttribute);
if ($userId) {
$userIdList = [$userId];
}
else {
$userIdList = [];
}
$userIdList = $userId ? [$userId] : [];
}
}
}
@@ -429,70 +444,98 @@ class Note extends Record
}
}
if ($usersAttributeIsChanged || $teamsAttributeIsChanged || $forceProcessNoteNotifications) {
$noteList = $this->entityManager
->getRepository('Note')
->where([
'OR' => [
[
'relatedId' => $entity->id,
'relatedType' => $entityType
],
[
'parentId' => $entity->id,
'parentType' => $entityType,
'superParentId!=' => null,
'relatedId' => null,
]
if (!$usersAttributeIsChanged && !$teamsAttributeIsChanged && !$forceProcessNoteNotifications) {
return;
}
$noteList = $this->entityManager
->getRepository('Note')
->where([
'OR' => [
[
'relatedId' => $entity->getId(),
'relatedType' => $entityType,
],
[
'parentId' => $entity->getId(),
'parentType' => $entityType,
'superParentId!=' => null,
'relatedId' => null,
]
])
->select([
'id',
'parentType',
'parentId',
'superParentType',
'superParentId',
'isInternal',
'relatedType',
'relatedId',
'createdAt',
])
->find();
]
])
->select([
'id',
'parentType',
'parentId',
'superParentType',
'superParentId',
'isInternal',
'relatedType',
'relatedId',
'createdAt',
])
->find();
$noteOptions = [];
$noteOptions = [];
if (!empty($forceProcessNoteNotifications)) {
$noteOptions['forceProcessNotifications'] = true;
}
if (!empty($forceProcessNoteNotifications)) {
$noteOptions['forceProcessNotifications'] = true;
}
$period = '-' . $this->getConfig()->get('noteNotificationPeriod', $this->noteNotificationPeriod);
$period = '-' . $this->getConfig()->get('noteNotificationPeriod', $this->noteNotificationPeriod);
$threshold = new DateTime();
$threshold = (new DateTime())->modify($period);
$threshold->modify($period);
foreach ($noteList as $note) {
if (!$entity->isNew()) {
try {
$createdAtDt = new DateTime($note->get('createdAt'));
if ($createdAtDt->getTimestamp() < $threshold->getTimestamp()) {
continue;
}
}
catch (Exception $e) {};
}
if ($teamsAttributeIsChanged || $forceProcessNoteNotifications) {
$note->set('teamsIds', $teamIdList);
}
if ($usersAttributeIsChanged || $forceProcessNoteNotifications) {
$note->set('usersIds', $userIdList);
}
$this->entityManager->saveEntity($note, $noteOptions);
}
foreach ($noteList as $note) {
$this->processNoteAclItem($entity, $note, [
'teamsAttributeIsChanged' => $teamsAttributeIsChanged,
'usersAttributeIsChanged' => $usersAttributeIsChanged,
'forceProcessNoteNotifications' => $forceProcessNoteNotifications,
'teamIdList' => $teamIdList,
'userIdList' => $userIdList,
'threshold' => $threshold,
]);
}
}
protected function processNoteAclItem(Entity $entity, NoteEntity $note, array $params): void
{
$teamsAttributeIsChanged = $params['teamsAttributeIsChanged'];
$usersAttributeIsChanged = $params['usersAttributeIsChanged'];
$forceProcessNoteNotifications = $params['forceProcessNoteNotifications'];
$teamIdList = $params['teamIdList'];
$userIdList = $params['userIdList'];
$threshold = $params['threshold'];
$noteOptions = [
'forceProcessNotifications' => $forceProcessNoteNotifications,
];
if (!$entity->isNew() && $note->get('createdAt')) {
try {
$createdAtDt = new DateTime($note->get('createdAt'));
}
catch (Exception $e) {
return;
}
if ($createdAtDt->getTimestamp() < $threshold->getTimestamp()) {
return;
}
}
if ($teamsAttributeIsChanged || $forceProcessNoteNotifications) {
$note->set('teamsIds', $teamIdList);
}
if ($usersAttributeIsChanged || $forceProcessNoteNotifications) {
$note->set('usersIds', $userIdList);
}
$this->entityManager->saveEntity($note, $noteOptions);
}
}
+48 -16
View File
@@ -179,6 +179,7 @@ class Stream
if (!$user) {
unset($userIdList[$i]);
continue;
}
@@ -223,7 +224,8 @@ class Stream
$userId = $this->user->id;
}
$isFollowed = (bool) $this->entityManager->getRepository('Subscription')
$isFollowed = (bool) $this->entityManager
->getRepository('Subscription')
->select(['id'])
->where([
'userId' => $userId,
@@ -1223,21 +1225,49 @@ class Stream
}
}
$ownerUserIdAttribute = $this->aclManager
->getImplementation($entity->getEntityType())
->getOwnerUserIdAttribute();
$ownerUserField = $this->aclManager->getReadOwnerUserField($entity->getEntityType());
if ($ownerUserIdAttribute && $entity->get($ownerUserIdAttribute)) {
if ($entity->getAttributeParam($ownerUserIdAttribute, 'isLinkMultipleIdList')) {
$userIdList = $entity->get($ownerUserIdAttribute);
}
else {
$userId = $entity->get($ownerUserIdAttribute);
$userIdList = [$userId];
}
$note->set('usersIds', $userIdList);
if (!$ownerUserField) {
return;
}
/* @var $defs \Espo\ORM\Defs\EntityDefs */
$defs = $this->entityManager->getDefs()->getEntity($entity->getEntityType());
if (!$defs->hasField($ownerUserField)) {
return;
}
$fieldDefs = $defs->getField($ownerUserField);
if ($fieldDefs->getType() === 'linkMultiple') {
$ownerUserIdAttribute = $ownerUserField . 'Ids';
}
else if ($fieldDefs->getType() === 'link') {
$ownerUserIdAttribute = $ownerUserField . 'Id';
}
else {
return;
}
if (!$entity->has($ownerUserIdAttribute)) {
return;
}
if ($fieldDefs->getType() === 'linkMultiple') {
$userIdList = $entity->getLinkMultipleIdList($ownerUserField);
}
else {
$userId = $entity->get($ownerUserIdAttribute);
if (!$userId) {
return;
}
$userIdList = [$userId];
}
$note->set('usersIds', $userIdList);
}
public function noteEmailReceived(Entity $entity, Entity $email, $isInitial = false): void
@@ -1379,6 +1409,7 @@ class Stream
$note->set('superParentId', $entity->get('accountId'));
$note->set('superParentType', 'Account');
// only if has super parent
$this->processNoteTeamsUsers($note, $entity);
}
@@ -1527,6 +1558,7 @@ class Stream
$note->set('superParentId', $entity->get('accountId'));
$note->set('superParentType', 'Account');
// only if has super parent
$this->processNoteTeamsUsers($note, $entity);
}
@@ -1839,7 +1871,7 @@ class Stream
}
if (
$this->aclManager->getLevel($user, $scope, 'read') === 'team'
$this->aclManager->checkReadOnlyTeam($user, $scope)
) {
$list[] = $scope;
}
@@ -1872,7 +1904,7 @@ class Stream
}
if (
$this->aclManager->getLevel($user, $scope, 'read') === 'own'
$this->aclManager->checkReadOnlyOwn($user, $scope)
) {
$list[] = $scope;
}
+12 -7
View File
@@ -35,24 +35,29 @@ use Espo\Core\{
class AclTest extends \tests\integration\Core\BaseTestCase
{
public function testGetOwnerUserIdAttribute()
public function testGetReadOwnerUserField()
{
/* @var $aclManager AclManager */
$aclManager = $this->getContainer()->get('aclManager');
$this->assertEquals(
'assignedUserId',
$aclManager->getImplementation('Account')->getOwnerUserIdAttribute()
'assignedUser',
$aclManager->getReadOwnerUserField('Account')
);
$this->assertEquals(
'createdById',
$aclManager->getImplementation('Import')->getOwnerUserIdAttribute()
'createdBy',
$aclManager->getReadOwnerUserField('Import')
);
$this->assertEquals(
'usersIds',
$aclManager->getImplementation('Email')->getOwnerUserIdAttribute()
'users',
$aclManager->getReadOwnerUserField('Email')
);
$this->assertEquals(
'users',
$aclManager->getReadOwnerUserField('Meeting')
);
}
}
+142
View File
@@ -0,0 +1,142 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\integration\Espo\Note;
use Espo\{
Services\Stream as StreamService,
Services\Note as NoteService,
ORM\EntityManager,
};
class AclTest extends \tests\integration\Core\BaseTestCase
{
public function testProcessNoteAcl()
{
/* @var $em EntityManager*/
$em = $this->getContainer()->get('entityManager');
/* @var $noteService NoteService*/
$noteService = $this->getContainer()->get('serviceFactory')->create('Note');
/* @var $streamService StreamService*/
$streamService = $this->getContainer()->get('serviceFactory')->create('Stream');
$team1 = $em->createEntity('Team', [
'name' => 'team-1',
]);
$team2 = $em->createEntity('Team', [
'name' => 'team-2',
]);
$user1 = $em->createEntity('User', [
'userName' => 'user-1',
'lastName' => 'user-1',
]);
$user2 = $em->createEntity('User', [
'userName' => 'user-2',
'lastName' => 'user-2',
]);
$account = $em->createEntity('Account', [
]);
// Opportunity
$opportunity = $em->createEntity('Opportunity', [
'assignedUserId' => $user1->getId(),
'teamsIds' => [$team1->getId()],
'accountId' => $account->getId(),
]);
$streamService->noteCreate($opportunity);
$note1 = $em
->getRDBRepository('Note')
->where([
'type' => 'Create',
'parentId' => $opportunity->getId(),
'parentType' => $opportunity->getEntityType(),
])
->findOne();
$this->assertEquals([$team1->getId()], $note1->getLinkMultipleIdList('teams'));
$this->assertEquals([$user1->getId()], $note1->getLinkMultipleIdList('users'));
$opportunity->set([
'assignedUserId' => $user2->getId(),
'teamsIds' => [$team2->getId()],
]);
$em->saveEntity($opportunity);
$note1 = $em->getEntity('Note', $note1->getId());
$this->assertEquals([$team2->getId()], $note1->getLinkMultipleIdList('teams'));
$this->assertEquals([$user2->getId()], $note1->getLinkMultipleIdList('users'));
// Meeting
$meeting = $em->createEntity('Meeting', [
'usersIds' => [$user1->getId()],
'teamsIds' => [$team1->getId()],
'parentId' => $account->getId(),
'parentType' => $account->getEntityType(),
]);
$streamService->noteRelate($meeting, $account->getEntityType(), $account->getId());
$note2 = $em
->getRDBRepository('Note')
->where([
'type' => 'Relate',
'relatedId' => $meeting->getId(),
'relatedType' => $meeting->getEntityType(),
])
->findOne();
$this->assertEquals([$team1->getId()], $note2->getLinkMultipleIdList('teams'));
$this->assertEquals([$user1->getId()], $note2->getLinkMultipleIdList('users'));
$meeting->set([
'usersIds' => [$user2->getId()],
'teamsIds' => [$team2->getId()],
]);
$em->saveEntity($meeting);
$note2 = $em->getEntity('Note', $note2->getId());
$this->assertEquals([$team2->getId()], $note2->getLinkMultipleIdList('teams'));
$this->assertEquals([$user2->getId()], $note2->getLinkMultipleIdList('users'));
}
}