mirror of
https://github.com/espocrm/espocrm.git
synced 2026-03-11 05:37:02 +00:00
Compare commits
304 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b22464c02f | ||
|
|
5dcfbf00d2 | ||
|
|
f4753f773f | ||
|
|
a6593f574e | ||
|
|
bff4e859cd | ||
|
|
d725cce8a7 | ||
|
|
a730f47ceb | ||
|
|
150e41a26d | ||
|
|
c500b30484 | ||
|
|
5266e8eeba | ||
|
|
e161b0da19 | ||
|
|
31a2ef63ec | ||
|
|
9f932462a8 | ||
|
|
a615eed985 | ||
|
|
df56679c4e | ||
|
|
23b2e87127 | ||
|
|
9391c68ad0 | ||
|
|
8a15278af2 | ||
|
|
8f2c12cba7 | ||
|
|
842c028fb5 | ||
|
|
27aae16634 | ||
|
|
0206da6d4f | ||
|
|
687f308b40 | ||
|
|
20eced4373 | ||
|
|
7d0c793e47 | ||
|
|
ead21ea2ee | ||
|
|
45ef1a4de5 | ||
|
|
28b866830e | ||
|
|
4d68e35f3b | ||
|
|
12a2069fcf | ||
|
|
b87d2459cc | ||
|
|
7c1c25577c | ||
|
|
9d6e0ba54b | ||
|
|
bd3c6867b5 | ||
|
|
28c4b8ea05 | ||
|
|
7a90e30243 | ||
|
|
f482c41e6c | ||
|
|
06ffafba10 | ||
|
|
2a07124d52 | ||
|
|
65247b1c69 | ||
|
|
10e4b7e5b6 | ||
|
|
ff3e6c34ba | ||
|
|
19700cfa00 | ||
|
|
d0de9e5bb9 | ||
|
|
9861653378 | ||
|
|
0a4525b75e | ||
|
|
1c45cadcf8 | ||
|
|
83ced0d58f | ||
|
|
6ee224ffbc | ||
|
|
a060f0732b | ||
|
|
4e2a824b1d | ||
|
|
85c4848614 | ||
|
|
a5ca838b7a | ||
|
|
1127f4b28c | ||
|
|
e68ff3c391 | ||
|
|
c257da0e70 | ||
|
|
7669ac90ae | ||
|
|
5e7ddcad1c | ||
|
|
1cf773216d | ||
|
|
da3283f2ba | ||
|
|
18d6a6767b | ||
|
|
f8fd222e25 | ||
|
|
fd7819f76a | ||
|
|
48cfedb777 | ||
|
|
9220ad8baa | ||
|
|
6207c8f547 | ||
|
|
b9e1660f92 | ||
|
|
8863e7da9b | ||
|
|
3b1462ac1a | ||
|
|
937503ab8b | ||
|
|
b3edfba0da | ||
|
|
01a1d26a52 | ||
|
|
4662fc47e9 | ||
|
|
5b543d778d | ||
|
|
798f56650d | ||
|
|
864c96fe56 | ||
|
|
4d5eecd680 | ||
|
|
db2216adb7 | ||
|
|
ae056f4a22 | ||
|
|
5178c2fbd0 | ||
|
|
4bedaef9a8 | ||
|
|
42d4de77ae | ||
|
|
3e0a8f0af1 | ||
|
|
33e29e20a2 | ||
|
|
d2a1e8f771 | ||
|
|
3a68ebcbf4 | ||
|
|
f496f386ee | ||
|
|
9e641e65ed | ||
|
|
d34bc6d7d2 | ||
|
|
7581169f87 | ||
|
|
7c53701df5 | ||
|
|
d273523c46 | ||
|
|
df6745ff79 | ||
|
|
9327b418bf | ||
|
|
5430fed1d1 | ||
|
|
10285cf3d3 | ||
|
|
c8bed9af32 | ||
|
|
7499703d77 | ||
|
|
4e1f421dc1 | ||
|
|
4ab2a0749c | ||
|
|
530ac9da18 | ||
|
|
4069d38978 | ||
|
|
4070b38190 | ||
|
|
718f3ddc9f | ||
|
|
e4be18ac51 | ||
|
|
fa63270530 | ||
|
|
54286984e9 | ||
|
|
523e4d2900 | ||
|
|
79af0d2cc7 | ||
|
|
4152ee10ea | ||
|
|
c41b92d15a | ||
|
|
5af9679751 | ||
|
|
c51fcf0cb6 | ||
|
|
41d4735502 | ||
|
|
7c3964c6f5 | ||
|
|
df744ba263 | ||
|
|
380b3dde0a | ||
|
|
4c696c8b21 | ||
|
|
95f9206880 | ||
|
|
e00557885f | ||
|
|
9aaab7d726 | ||
|
|
ab9f78d33f | ||
|
|
ac57f6a39a | ||
|
|
49077225c6 | ||
|
|
f3a13739c0 | ||
|
|
ae4d37498a | ||
|
|
25a607952c | ||
|
|
51cfbb0779 | ||
|
|
15c97e5d12 | ||
|
|
4847bc019a | ||
|
|
094f6e2f50 | ||
|
|
6cc19a52f1 | ||
|
|
f0237005c0 | ||
|
|
3aa31a830c | ||
|
|
c61513cf84 | ||
|
|
d10b6dd9b3 | ||
|
|
b466f91f8a | ||
|
|
7081440121 | ||
|
|
5042f58e30 | ||
|
|
beb51081d3 | ||
|
|
1997da4343 | ||
|
|
0b3f3e67dd | ||
|
|
e9d7b92a6e | ||
|
|
334d5609b0 | ||
|
|
6e31c591bd | ||
|
|
d125d8f067 | ||
|
|
49b55236d0 | ||
|
|
093001bf95 | ||
|
|
3445f87e0c | ||
|
|
8d3ba52501 | ||
|
|
82a7590a2a | ||
|
|
773df7aedd | ||
|
|
424ca82b15 | ||
|
|
e5df6ad7bf | ||
|
|
d81f8f7bed | ||
|
|
7203b652fa | ||
|
|
5d5268c2c7 | ||
|
|
b568e96407 | ||
|
|
1dda881422 | ||
|
|
17d2791d40 | ||
|
|
9469b66c61 | ||
|
|
bbc16b942e | ||
|
|
2cb235f49c | ||
|
|
e2a7a439d2 | ||
|
|
2c19ebf570 | ||
|
|
19d799ac5e | ||
|
|
e89a0955ea | ||
|
|
fc6dfd1994 | ||
|
|
3fa33b6706 | ||
|
|
939daa616b | ||
|
|
fd22f50de2 | ||
|
|
48ca9259af | ||
|
|
c49d3d4c30 | ||
|
|
246b0c2a37 | ||
|
|
33f446401b | ||
|
|
9c677463f2 | ||
|
|
786e267e93 | ||
|
|
c782743a24 | ||
|
|
b2c4c76fba | ||
|
|
5a6660b60d | ||
|
|
f5d324eb48 | ||
|
|
901e0967fc | ||
|
|
1cfacc3813 | ||
|
|
8f1c7d5d54 | ||
|
|
3427c1b0bd | ||
|
|
4a12d44971 | ||
|
|
d833d43a8b | ||
|
|
64a3b851b6 | ||
|
|
2a0ceceb2f | ||
|
|
7250e0e5c4 | ||
|
|
40161fdc3c | ||
|
|
cb815d7dea | ||
|
|
fffe4aacc1 | ||
|
|
985c12c918 | ||
|
|
87bbf61c66 | ||
|
|
8f204de057 | ||
|
|
419b08070a | ||
|
|
ba1d4d7b72 | ||
|
|
a48b5df28e | ||
|
|
07bbc42df5 | ||
|
|
14e77571c0 | ||
|
|
71a35878c1 | ||
|
|
7dc74f0767 | ||
|
|
784d12f3ca | ||
|
|
cdf9700ff0 | ||
|
|
05f926b33b | ||
|
|
b05f2acc34 | ||
|
|
67b52d1f42 | ||
|
|
6391c3ca08 | ||
|
|
fc59a1efa1 | ||
|
|
e215e39e5d | ||
|
|
33e1f48717 | ||
|
|
94f61e5aff | ||
|
|
41b25a43ba | ||
|
|
09e617ee78 | ||
|
|
f5f8b2f66a | ||
|
|
45b9b41557 | ||
|
|
e0a1b983e3 | ||
|
|
4d2b339112 | ||
|
|
b1a66e4a20 | ||
|
|
1e44f82b38 | ||
|
|
487f428c7f | ||
|
|
f5fa51854d | ||
|
|
3f15295293 | ||
|
|
de72c2b055 | ||
|
|
8f93507fec | ||
|
|
2afaf8b446 | ||
|
|
33e0a1b228 | ||
|
|
4b7dd1fd22 | ||
|
|
6f7ef14482 | ||
|
|
097a115595 | ||
|
|
cd78458d19 | ||
|
|
b69d706cc4 | ||
|
|
8286d7b621 | ||
|
|
d899c55f4f | ||
|
|
0a954e171d | ||
|
|
89d2c38257 | ||
|
|
d7829e7801 | ||
|
|
3d4f6055e4 | ||
|
|
383ae3fe3e | ||
|
|
47cbd18c88 | ||
|
|
d5779878c7 | ||
|
|
c65cabffd5 | ||
|
|
fe90a62239 | ||
|
|
13ae33266f | ||
|
|
399b689bcd | ||
|
|
1219e7be12 | ||
|
|
10f67ab360 | ||
|
|
ce1f755612 | ||
|
|
fa943d1166 | ||
|
|
dc09f91e35 | ||
|
|
34b8fa2728 | ||
|
|
02f7bebf67 | ||
|
|
4ffc384764 | ||
|
|
e1ef2b34ea | ||
|
|
d5a83a8819 | ||
|
|
661f499f67 | ||
|
|
a073beeea7 | ||
|
|
07f6bf7a7a | ||
|
|
0cfbb269f2 | ||
|
|
f3791e5fb4 | ||
|
|
37d8b0d3bd | ||
|
|
4ae383033a | ||
|
|
ea71675701 | ||
|
|
1178d01baa | ||
|
|
ace53f9494 | ||
|
|
3c17ff2da9 | ||
|
|
163991a36b | ||
|
|
291721c1d0 | ||
|
|
d96301d544 | ||
|
|
54d9674055 | ||
|
|
4a5a07649a | ||
|
|
cfdb65fad8 | ||
|
|
16544a4a70 | ||
|
|
02939d0840 | ||
|
|
bf331c7020 | ||
|
|
e80b55572b | ||
|
|
51c29f0a6e | ||
|
|
056191db82 | ||
|
|
9ace150efa | ||
|
|
79ae89d172 | ||
|
|
bb703e2060 | ||
|
|
972d9019e1 | ||
|
|
c99566410a | ||
|
|
9c56a14fb0 | ||
|
|
b95e58317b | ||
|
|
6fd9d6e15c | ||
|
|
518b9fa2ba | ||
|
|
c113b7bd88 | ||
|
|
fb0a302f62 | ||
|
|
43127379cd | ||
|
|
c5bc7136c7 | ||
|
|
18ba671417 | ||
|
|
6141bfd1fb | ||
|
|
4e4b51c4f1 | ||
|
|
83ac300aa3 | ||
|
|
f641a3c9c6 | ||
|
|
18a4782bf1 | ||
|
|
f1fddb7325 | ||
|
|
9c864af409 | ||
|
|
7cd332228a | ||
|
|
54c534736f | ||
|
|
6f07862572 | ||
|
|
54863ca6a4 |
@@ -110,6 +110,7 @@ module.exports = grunt => {
|
||||
'build/tmp/client/custom/*',
|
||||
'!build/tmp/client/custom/modules',
|
||||
'build/tmp/client/custom/modules/*',
|
||||
'!build/tmp/client/custom/modules/dummy.txt',
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Classes\Acl\Attachment;
|
||||
use Espo\Entities\{
|
||||
User,
|
||||
Note,
|
||||
Attachment,
|
||||
};
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
@@ -45,6 +46,9 @@ use Espo\Core\{
|
||||
Acl\Traits\DefaultAccessCheckerDependency,
|
||||
};
|
||||
|
||||
/**
|
||||
* @implements AccessEntityCREDChecker<Attachment>
|
||||
*/
|
||||
class AccessChecker implements AccessEntityCREDChecker
|
||||
{
|
||||
use DefaultAccessCheckerDependency;
|
||||
@@ -65,7 +69,10 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var Attachment $entity */
|
||||
|
||||
if ($entity->get('parentType') === 'Settings') {
|
||||
// Allow the logo.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -93,6 +100,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
|
||||
if ($parent->getEntityType() === 'Note') {
|
||||
/** @var Note $parent */
|
||||
$result = $this->checkEntityReadNoteParent($user, $parent);
|
||||
|
||||
if ($result !== null) {
|
||||
@@ -100,6 +108,16 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
}
|
||||
else if ($this->aclManager->checkEntity($user, $parent)) {
|
||||
if (
|
||||
$entity->getTargetField() &&
|
||||
in_array(
|
||||
$entity->getTargetField(),
|
||||
$this->aclManager->getScopeForbiddenFieldList($user, $parent->getEntityType())
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Classes\Acl\Email;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Email;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
@@ -52,6 +53,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var Email $entity */
|
||||
|
||||
if ($this->defaultAccessChecker->checkEntityRead($user, $entity, $data)) {
|
||||
return true;
|
||||
}
|
||||
@@ -79,6 +82,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var Email $entity */
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Classes\Acl\Email;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Email;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
@@ -50,6 +51,8 @@ class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
|
||||
|
||||
public function checkOwn(User $user, Entity $entity): bool
|
||||
{
|
||||
/** @var Email $entity */
|
||||
|
||||
if ($user->getId() === $entity->get('assignedUserId')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@ use Espo\Core\{
|
||||
AclManager,
|
||||
};
|
||||
|
||||
/**
|
||||
* @implements AccessEntityCREDSChecker<User>
|
||||
*/
|
||||
class AccessChecker implements AccessEntityCREDSChecker
|
||||
{
|
||||
use DefaultAccessCheckerDependency;
|
||||
@@ -62,6 +65,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isSuperAdmin() && !$user->isSuperAdmin()) {
|
||||
return false;
|
||||
}
|
||||
@@ -71,6 +76,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isPortal()) {
|
||||
if ($this->aclManager->getPermissionLevel($user, 'portal') === Table::LEVEL_YES) {
|
||||
return true;
|
||||
@@ -88,6 +95,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if ($entity->isSystem()) {
|
||||
return false;
|
||||
}
|
||||
@@ -107,6 +116,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
if (!$user->isAdmin()) {
|
||||
return false;
|
||||
}
|
||||
@@ -124,6 +135,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var User $entity */
|
||||
|
||||
return $this->aclManager->checkUserPermission($user, $entity, 'user');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
namespace Espo\Classes\Acl\User;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\Core\{
|
||||
Acl\OwnershipOwnChecker,
|
||||
@@ -47,6 +47,8 @@ class OwnershipChecker implements OwnershipOwnChecker, OwnershipTeamChecker
|
||||
|
||||
public function checkTeam(User $user, Entity $entity): bool
|
||||
{
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$intersect = array_intersect(
|
||||
$user->getLinkMultipleIdList('teams'),
|
||||
$entity->getLinkMultipleIdList('teams')
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Espo\Classes\AclPortal\Attachment;
|
||||
use Espo\Entities\{
|
||||
User,
|
||||
Note,
|
||||
Attachment,
|
||||
};
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
@@ -45,6 +46,9 @@ use Espo\Core\{
|
||||
Portal\Acl\Traits\DefaultAccessCheckerDependency,
|
||||
};
|
||||
|
||||
/**
|
||||
* @implements AccessEntityCREDChecker<Attachment>
|
||||
*/
|
||||
class AccessChecker implements AccessEntityCREDChecker
|
||||
{
|
||||
use DefaultAccessCheckerDependency;
|
||||
@@ -67,7 +71,10 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
{
|
||||
/** @var Attachment $entity */
|
||||
|
||||
if ($entity->get('parentType') === 'Settings') {
|
||||
// Allow the logo.
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -94,6 +101,7 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
|
||||
if ($parent->getEntityType() === 'Note') {
|
||||
/** @var Note $parent */
|
||||
$result = $this->checkEntityReadNoteParent($user, $parent);
|
||||
|
||||
if ($result !== null) {
|
||||
@@ -101,6 +109,16 @@ class AccessChecker implements AccessEntityCREDChecker
|
||||
}
|
||||
}
|
||||
else if ($this->aclManager->checkEntity($user, $parent)) {
|
||||
if (
|
||||
$entity->getTargetField() &&
|
||||
in_array(
|
||||
$entity->getTargetField(),
|
||||
$this->aclManager->getScopeForbiddenFieldList($user, $parent->getEntityType())
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@ use Espo\Entities\User;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\Core\{
|
||||
Portal\AclManager,
|
||||
Acl\Table,
|
||||
Acl\ScopeData,
|
||||
Acl\AccessEntityCREDSChecker,
|
||||
@@ -48,14 +49,10 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
|
||||
private $defaultAccessChecker;
|
||||
|
||||
private $aclManager;
|
||||
|
||||
public function __construct(
|
||||
DefaultAccessChecker $defaultAccessChecker,
|
||||
AclManager $aclManager
|
||||
DefaultAccessChecker $defaultAccessChecker
|
||||
) {
|
||||
$this->defaultAccessChecker = $defaultAccessChecker;
|
||||
$this->aclManager = $aclManager;
|
||||
}
|
||||
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool
|
||||
@@ -72,6 +69,8 @@ class AccessChecker implements AccessEntityCREDSChecker
|
||||
return false;
|
||||
}
|
||||
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$userIdList = $entity->getLinkMultipleIdLIst('users');
|
||||
|
||||
if (is_array($userIdList) && in_array($user->getId(), $userIdList)) {
|
||||
|
||||
@@ -69,7 +69,7 @@ class Binding
|
||||
$keyList = $data->getContextKeyList($context);
|
||||
|
||||
foreach ($keyList as $key) {
|
||||
$result .= $this->printItem($key, $data->getContext($context, $key), true);
|
||||
$result .= $this->printItem($key, $data->getContext($context, $key));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,19 @@ use Espo\Core\{
|
||||
*/
|
||||
class TemplateEntityTypeList
|
||||
{
|
||||
/**
|
||||
* @var Acl
|
||||
*/
|
||||
protected $acl;
|
||||
|
||||
/**
|
||||
* @var SelectBuilderFactory
|
||||
*/
|
||||
protected $selectBuilderFactory;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(Acl $acl, SelectBuilderFactory $selectBuilderFactory, EntityManager $entityManager)
|
||||
@@ -51,7 +62,7 @@ class TemplateEntityTypeList
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function get() : array
|
||||
public function get(): array
|
||||
{
|
||||
if (!$this->acl->checkScope('Template')) {
|
||||
return [];
|
||||
@@ -69,7 +80,7 @@ class TemplateEntityTypeList
|
||||
->build();
|
||||
|
||||
$templateCollection = $this->entityManager
|
||||
->getRepository('Template')
|
||||
->getRDBRepository('Template')
|
||||
->clone($query)
|
||||
->find();
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Notification;
|
||||
use Espo\Entities\Email as EmailEntity;
|
||||
|
||||
use Espo\Repositories\Email as EmailRepository;
|
||||
use Espo\Repositories\EmailAddress as EmailAddressRepository;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
@@ -79,6 +83,8 @@ class Email implements AssignmentNotificator
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/** @var EmailEntity $entity */
|
||||
|
||||
if (!in_array($entity->get('status'), ['Archived', 'Sent', 'Being Imported'])) {
|
||||
return;
|
||||
}
|
||||
@@ -107,10 +113,6 @@ class Email implements AssignmentNotificator
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$dt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($dt->diff(new DateTime())->days > self::DAYS_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
@@ -138,12 +140,17 @@ class Email implements AssignmentNotificator
|
||||
'emailName' => $entity->get('name'),
|
||||
];
|
||||
|
||||
/** @var EmailRepository $emailRepository */
|
||||
$emailRepository = $this->entityManager->getRepository('Email');
|
||||
/** @var EmailAddressRepository $emailAddressRepository */
|
||||
$emailAddressRepository = $this->entityManager->getRepository('EmailAddress');
|
||||
|
||||
if (!$entity->has('from')) {
|
||||
$this->entityManager->getRepository('Email')->loadFromField($entity);
|
||||
$emailRepository->loadFromField($entity);
|
||||
}
|
||||
|
||||
if (!$entity->has('to')) {
|
||||
$this->entityManager->getRepository('Email')->loadToField($entity);
|
||||
$emailRepository->loadToField($entity);
|
||||
}
|
||||
|
||||
$person = null;
|
||||
@@ -151,9 +158,7 @@ class Email implements AssignmentNotificator
|
||||
$from = $entity->get('from');
|
||||
|
||||
if ($from) {
|
||||
$person = $this->entityManager
|
||||
->getRepository('EmailAddress')
|
||||
->getEntityByAddress($from, null, ['User', 'Contact', 'Lead']);
|
||||
$person = $emailAddressRepository->getEntityByAddress($from, null, ['User', 'Contact', 'Lead']);
|
||||
|
||||
if ($person) {
|
||||
$data['personEntityType'] = $person->getEntityType();
|
||||
@@ -165,7 +170,7 @@ class Email implements AssignmentNotificator
|
||||
$userIdFrom = null;
|
||||
|
||||
if ($person && $person->getEntityType() === 'User') {
|
||||
$userIdFrom = $person->id;
|
||||
$userIdFrom = $person->getId();
|
||||
}
|
||||
|
||||
if (empty($data['personEntityId'])) {
|
||||
@@ -214,7 +219,7 @@ class Email implements AssignmentNotificator
|
||||
if ($folderId) {
|
||||
if (
|
||||
$this->entityManager
|
||||
->getRepository('EmailFolder')
|
||||
->getRDBRepository('EmailFolder')
|
||||
->where([
|
||||
'id' => $folderId,
|
||||
'skipNotifications' => true,
|
||||
@@ -226,6 +231,7 @@ class Email implements AssignmentNotificator
|
||||
}
|
||||
}
|
||||
|
||||
/** @var User|null $user */
|
||||
$user = $this->entityManager->getEntity('User', $userId);
|
||||
|
||||
if (!$user) {
|
||||
@@ -261,7 +267,7 @@ class Email implements AssignmentNotificator
|
||||
}
|
||||
|
||||
$existing = $this->entityManager
|
||||
->getRepository(Notification::ENTITY_TYPE)
|
||||
->getRDBRepository(Notification::ENTITY_TYPE)
|
||||
->where([
|
||||
'type' => Notification::TYPE_EMAIL_RECEIVED,
|
||||
'userId' => $userId,
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
namespace Espo\Classes\DuplicateWhereBuilders;
|
||||
|
||||
use Espo\Core\Duplicate\WhereBuilder;
|
||||
use Espo\Core\Field\EmailAddressGroup;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\ORM\{
|
||||
Query\Part\Condition as Cond,
|
||||
@@ -42,6 +44,8 @@ class Company implements WhereBuilder
|
||||
{
|
||||
public function build(Entity $entity): ?WhereItem
|
||||
{
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$orBuilder = OrGroup::createBuilder();
|
||||
|
||||
$toCheck = false;
|
||||
@@ -84,10 +88,10 @@ class Company implements WhereBuilder
|
||||
return $orBuilder->build();
|
||||
}
|
||||
|
||||
private function getEmailAddressList(Entity $entity): array
|
||||
private function getEmailAddressList(CoreEntity $entity): array
|
||||
{
|
||||
if ($entity->get('emailAddressData')) {
|
||||
/* @var $eaGroup EmailAddressGroup */
|
||||
/** @var EmailAddressGroup $eaGroup */
|
||||
$eaGroup = $entity->getValueObject('emailAddress');
|
||||
|
||||
return $eaGroup->getAddressList();
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Classes\DuplicateWhereBuilders;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\Core\{
|
||||
Duplicate\WhereBuilder,
|
||||
Field\EmailAddressGroup,
|
||||
@@ -45,6 +47,8 @@ class Person implements WhereBuilder
|
||||
{
|
||||
public function build(Entity $entity): ?WhereItem
|
||||
{
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$orBuilder = OrGroup::createBuilder();
|
||||
|
||||
$toCheck = false;
|
||||
@@ -93,10 +97,10 @@ class Person implements WhereBuilder
|
||||
return $orBuilder->build();
|
||||
}
|
||||
|
||||
private function getEmailAddressList(Entity $entity): array
|
||||
private function getEmailAddressList(CoreEntity $entity): array
|
||||
{
|
||||
if ($entity->get('emailAddressData')) {
|
||||
/* @var $eaGroup EmailAddressGroup */
|
||||
/** @var EmailAddressGroup $eaGroup */
|
||||
$eaGroup = $entity->getValueObject('emailAddress');
|
||||
|
||||
return $eaGroup->getAddressList();
|
||||
|
||||
@@ -39,6 +39,9 @@ use Espo\Core\{
|
||||
|
||||
use Espo\Repositories\Email as EmailRepository;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Email>
|
||||
*/
|
||||
class AddressDataLoader implements Loader
|
||||
{
|
||||
private $entityManager;
|
||||
@@ -50,7 +53,7 @@ class AddressDataLoader implements Loader
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/* @var $repository EmailRepository */
|
||||
/** @var EmailRepository $repository */
|
||||
$repository = $this->entityManager->getRepository('Email');
|
||||
|
||||
$repository->loadFromField($entity);
|
||||
|
||||
@@ -114,7 +114,7 @@ class IcsDataLoader implements Loader
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var $emailAddressRepository EmailAddressRepository */
|
||||
/** @var EmailAddressRepository $emailAddressRepository */
|
||||
$emailAddressRepository = $this->entityManager->getRepository(EmailAddress::ENTITY_TYPE);
|
||||
|
||||
$attendeeEmailAddressList = $espoEvent->getAttendeeEmailAddressList();
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Classes\FieldProcessing\Email;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Repositories\EmailAddress as EmailAddressRepository;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
@@ -56,6 +58,8 @@ class StringDataLoader implements Loader
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/** @var Email $entity */
|
||||
|
||||
$userEmailAdddressIdList = [];
|
||||
|
||||
$emailAddressCollection = $this->entityManager
|
||||
@@ -84,9 +88,7 @@ class StringDataLoader implements Loader
|
||||
$list = [];
|
||||
|
||||
foreach ($idList as $emailAddressId) {
|
||||
$person = $this->entityManager
|
||||
->getRepository('EmailAddress')
|
||||
->getEntityByAddressId($emailAddressId, null, true);
|
||||
$person = $this->getEmailAddressRepository()->getEntityByAddressId($emailAddressId, null, true);
|
||||
|
||||
$list[] = $person ? $person->get('name') : $names->$emailAddressId;
|
||||
}
|
||||
@@ -103,9 +105,7 @@ class StringDataLoader implements Loader
|
||||
}
|
||||
|
||||
if (!array_key_exists($fromEmailAddressId, $this->fromEmailAddressNameCache)) {
|
||||
$person = $this->entityManager
|
||||
->getRepository('EmailAddress')
|
||||
->getEntityByAddressId($fromEmailAddressId, null, true);
|
||||
$person = $this->getEmailAddressRepository()->getEntityByAddressId($fromEmailAddressId, null, true);
|
||||
|
||||
$fromName = $person ? $person->get('name') : null;
|
||||
|
||||
@@ -119,4 +119,10 @@ class StringDataLoader implements Loader
|
||||
|
||||
$entity->set('personStringData', $fromName);
|
||||
}
|
||||
|
||||
private function getEmailAddressRepository(): EmailAddressRepository
|
||||
{
|
||||
/** @var EmailAddressRepository */
|
||||
return $this->entityManager->getRepository('EmailAddress');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class UserColumnsLoader implements Loader
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$emailUser = $this->entityManager
|
||||
->getRepository('EmailUser')
|
||||
->getRDBRepository('EmailUser')
|
||||
->select(['isRead', 'isImportant', 'inTrash'])
|
||||
->where([
|
||||
'deleted' => false,
|
||||
|
||||
@@ -39,6 +39,9 @@ use Espo\Core\{
|
||||
|
||||
use Espo\Repositories\Import as ImportRepository;
|
||||
|
||||
/**
|
||||
* @implements Loader<\Espo\Entities\Import>
|
||||
*/
|
||||
class CountsLoader implements Loader
|
||||
{
|
||||
private $entityManager;
|
||||
@@ -50,7 +53,7 @@ class CountsLoader implements Loader
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/* @var $repository ImportRepository */
|
||||
/** @var ImportRepository $repository */
|
||||
$repository = $this->entityManager->getRepository('Import');
|
||||
|
||||
$importedCount = $repository->countResultRecords($entity, 'imported');
|
||||
|
||||
@@ -42,7 +42,7 @@ class AttachmentsLoader implements Loader
|
||||
{
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
/* @var $entity Note */
|
||||
/** @var Note $entity */
|
||||
$entity->loadAttachments();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Classes\FieldProcessing\Portal;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Repositories\Portal as PortalRepository;
|
||||
|
||||
use Espo\Core\{
|
||||
FieldProcessing\Loader,
|
||||
FieldProcessing\Loader\Params,
|
||||
@@ -48,8 +50,12 @@ class UrlLoader implements Loader
|
||||
|
||||
public function process(Entity $entity, Params $params): void
|
||||
{
|
||||
$this->entityManager
|
||||
->getRepository('Portal')
|
||||
->loadUrlField($entity);
|
||||
$this->getPortalRepository()->loadUrlField($entity);
|
||||
}
|
||||
|
||||
private function getPortalRepository(): PortalRepository
|
||||
{
|
||||
/** @var PortalRepository */
|
||||
return $this->entityManager->getRepository('Portal');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,16 @@
|
||||
namespace Espo\Classes\FieldValidators;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
class LinkMultipleType
|
||||
{
|
||||
public function checkRequired(Entity $entity, string $field): bool
|
||||
{
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return count($entity->getLinkMultipleIdList($field)) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class AuthTokenControl implements JobDataLess
|
||||
}
|
||||
|
||||
$tokenList = $this->entityManager
|
||||
->getRepository('AuthToken')
|
||||
->getRDBRepository('AuthToken')
|
||||
->where($whereClause)
|
||||
->limit(0, 500)
|
||||
->find();
|
||||
|
||||
@@ -40,8 +40,14 @@ use DateTimeZone;
|
||||
|
||||
class CheckNewVersion implements JobDataLess
|
||||
{
|
||||
/**
|
||||
* @var Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(Config $config, EntityManager $entityManager)
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
namespace Espo\Classes\Jobs;
|
||||
|
||||
use Espo\Core\Record\ServiceContainer;
|
||||
use Espo\ORM\Repository\RDBRepository;
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Config,
|
||||
@@ -183,14 +185,16 @@ class Cleanup implements JobDataLess
|
||||
|
||||
private function cleanupScheduledJobLog(): void
|
||||
{
|
||||
$scheduledJobList = $this->entityManager->getRepository('ScheduledJob')
|
||||
$scheduledJobList = $this->entityManager
|
||||
->getRDBRepository('ScheduledJob')
|
||||
->select(['id'])
|
||||
->find();
|
||||
|
||||
foreach ($scheduledJobList as $scheduledJob) {
|
||||
$scheduledJobId = $scheduledJob->get('id');
|
||||
|
||||
$ignoreLogRecordList = $this->entityManager->getRepository('ScheduledJobLogRecord')
|
||||
$ignoreLogRecordList = $this->entityManager
|
||||
->getRDBRepository('ScheduledJobLogRecord')
|
||||
->select(['id'])
|
||||
->where([
|
||||
'scheduledJobId' => $scheduledJobId,
|
||||
@@ -204,6 +208,7 @@ class Cleanup implements JobDataLess
|
||||
}
|
||||
|
||||
$ignoreIdList = [];
|
||||
|
||||
foreach ($ignoreLogRecordList as $logRecord) {
|
||||
$ignoreIdList[] = $logRecord->get('id');
|
||||
}
|
||||
@@ -300,7 +305,7 @@ class Cleanup implements JobDataLess
|
||||
$datetime->modify($period);
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRepository('Attachment')
|
||||
->getRDBRepository('Attachment')
|
||||
->where([
|
||||
'OR' => [
|
||||
[
|
||||
@@ -329,7 +334,7 @@ class Cleanup implements JobDataLess
|
||||
]);
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRepository('Attachment')
|
||||
->getRDBRepository('Attachment')
|
||||
->clone($orphanQueryBuilder->build())
|
||||
->limit(0, 5000)
|
||||
->find();
|
||||
@@ -398,7 +403,8 @@ class Cleanup implements JobDataLess
|
||||
continue;
|
||||
}
|
||||
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->select()
|
||||
->from($scope)
|
||||
->withDeleted()
|
||||
@@ -415,16 +421,16 @@ class Cleanup implements JobDataLess
|
||||
|
||||
foreach ($deletedEntityList as $deletedEntity) {
|
||||
$attachmentToRemoveList = $this->entityManager
|
||||
->getRepository('Attachment')
|
||||
->getRDBRepository('Attachment')
|
||||
->where([
|
||||
'OR' => [
|
||||
[
|
||||
'relatedType' => $scope,
|
||||
'relatedId' => $deletedEntity->id,
|
||||
'relatedId' => $deletedEntity->getId(),
|
||||
],
|
||||
[
|
||||
'parentType' => $scope,
|
||||
'parentId' => $deletedEntity->id,
|
||||
'parentId' => $deletedEntity->getId(),
|
||||
]
|
||||
]
|
||||
])
|
||||
@@ -436,7 +442,9 @@ class Cleanup implements JobDataLess
|
||||
}
|
||||
}
|
||||
|
||||
$delete = $this->entityManager->getQueryBuilder()->delete()
|
||||
$delete = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from('Attachment')
|
||||
->where([
|
||||
'deleted' => true,
|
||||
@@ -451,14 +459,15 @@ class Cleanup implements JobDataLess
|
||||
{
|
||||
$dateBefore = date('Y-m-d H:i:s', time() - 3600 * 24 * 20);
|
||||
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->select()
|
||||
->from('Email')
|
||||
->withDeleted()
|
||||
->build();
|
||||
|
||||
$emailList = $this->entityManager
|
||||
->getRepository('Email')
|
||||
->getRDBRepository('Email')
|
||||
->clone($query)
|
||||
->select(['id'])
|
||||
->where([
|
||||
@@ -471,7 +480,7 @@ class Cleanup implements JobDataLess
|
||||
$id = $email->get('id');
|
||||
|
||||
$attachments = $this->entityManager
|
||||
->getRepository('Attachment')
|
||||
->getRDBRepository('Attachment')
|
||||
->where([
|
||||
'parentId' => $id,
|
||||
'parentType' => 'Email'
|
||||
@@ -482,7 +491,9 @@ class Cleanup implements JobDataLess
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
}
|
||||
|
||||
$delete = $this->entityManager->getQueryBuilder()->delete()
|
||||
$delete = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from('Email')
|
||||
->where([
|
||||
'deleted' => true,
|
||||
@@ -492,7 +503,9 @@ class Cleanup implements JobDataLess
|
||||
|
||||
$this->entityManager->getQueryExecutor()->execute($delete);
|
||||
|
||||
$delete = $this->entityManager->getQueryBuilder()->delete()
|
||||
$delete = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from('EmailUser')
|
||||
->where([
|
||||
'emailId' => $id,
|
||||
@@ -510,14 +523,17 @@ class Cleanup implements JobDataLess
|
||||
$datetime = new DateTime();
|
||||
$datetime->modify($period);
|
||||
|
||||
$notificationList = $this->entityManager->getRepository('Notification')
|
||||
$notificationList = $this->entityManager
|
||||
->getRDBRepository('Notification')
|
||||
->where([
|
||||
'DATE:createdAt<' => $datetime->format('Y-m-d'),
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($notificationList as $notification) {
|
||||
$this->entityManager->getRepository('Notification')->deleteFromDb($notification->get('id'));
|
||||
$this->entityManager
|
||||
->getRDBRepository('Notification')
|
||||
->deleteFromDb($notification->get('id'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,7 +569,15 @@ class Cleanup implements JobDataLess
|
||||
|
||||
$repository = $this->entityManager->getRepository($scope);
|
||||
|
||||
$repository->deleteFromDb($entity->id);
|
||||
if (!$repository instanceof RDBRepository) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return;
|
||||
}
|
||||
|
||||
$repository->deleteFromDb($entity->getId());
|
||||
|
||||
$query = $this->entityManager->getQueryComposer();
|
||||
|
||||
@@ -576,7 +600,7 @@ class Cleanup implements JobDataLess
|
||||
}
|
||||
|
||||
$where = [
|
||||
$midKey => $entity->id,
|
||||
$midKey => $entity->getId(),
|
||||
];
|
||||
|
||||
$conditions = $entity->getRelationParam($relation, 'conditions') ?? [];
|
||||
@@ -585,17 +609,15 @@ class Cleanup implements JobDataLess
|
||||
$where[$key] = $value;
|
||||
}
|
||||
|
||||
if (empty($where)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$relationEntityType = ucfirst($relationName);
|
||||
|
||||
if (!$this->entityManager->hasRepository($relationEntityType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$delete = $this->entityManager->getQueryBuilder()->delete()
|
||||
$delete = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->delete()
|
||||
->from($relationEntityType)
|
||||
->where($where)
|
||||
->build();
|
||||
@@ -607,24 +629,25 @@ class Cleanup implements JobDataLess
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->select()
|
||||
->from('Note')
|
||||
->withDeleted()
|
||||
->build();
|
||||
|
||||
$noteList = $this->entityManager
|
||||
->getRepository('Note')
|
||||
->getRDBRepository('Note')
|
||||
->clone($query)
|
||||
->where([
|
||||
'OR' => [
|
||||
[
|
||||
'relatedType' => $scope,
|
||||
'relatedId' => $entity->id,
|
||||
'relatedId' => $entity->getId(),
|
||||
],
|
||||
[
|
||||
'parentType' => $scope,
|
||||
'parentId' => $entity->id,
|
||||
'parentId' => $entity->getId(),
|
||||
]
|
||||
]
|
||||
])
|
||||
@@ -640,29 +663,33 @@ class Cleanup implements JobDataLess
|
||||
|
||||
if ($scope === 'Note') {
|
||||
$attachmentList = $this->entityManager
|
||||
->getRepository('Attachment')
|
||||
->getRDBRepository('Attachment')
|
||||
->where([
|
||||
'parentId' => $entity->id,
|
||||
'parentId' => $entity->getId(),
|
||||
'parentType' => 'Note',
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($attachmentList as $attachment) {
|
||||
$this->entityManager->removeEntity($attachment);
|
||||
$this->entityManager->getRepository('Attachment')->deleteFromDb($attachment->id);
|
||||
$this->entityManager
|
||||
->getRDBRepository('Attachment')
|
||||
->deleteFromDb($attachment->getId());
|
||||
}
|
||||
}
|
||||
|
||||
$arrayValueList = $this->entityManager
|
||||
->getRepository('ArrayValue')
|
||||
->getRDBRepository('ArrayValue')
|
||||
->where([
|
||||
'entityType' => $entity->getEntityType(),
|
||||
'entityId' => $entity->id,
|
||||
'entityId' => $entity->getId(),
|
||||
])
|
||||
->find();
|
||||
|
||||
foreach ($arrayValueList as $arrayValue) {
|
||||
$this->entityManager->getRepository('ArrayValue')->deleteFromDb($arrayValue->id);
|
||||
$this->entityManager
|
||||
->getRDBRepository('ArrayValue')
|
||||
->deleteFromDb($arrayValue->getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,24 +720,12 @@ class Cleanup implements JobDataLess
|
||||
|
||||
$repository = $this->entityManager->getRepository($scope);
|
||||
|
||||
if (!$repository) {
|
||||
if (!$repository instanceof RDBRepository) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method_exists($repository, 'find')) continue;
|
||||
if (!method_exists($repository, 'clone')) continue;
|
||||
if (!method_exists($repository, 'where')) continue;
|
||||
if (!method_exists($repository, 'select')) continue;
|
||||
if (!method_exists($repository, 'deleteFromDb')) continue;
|
||||
|
||||
$hasCleanupMethod = false;
|
||||
|
||||
$service = $this->recordServiceContainer->get($scope);
|
||||
|
||||
if (method_exists($service, 'cleanup')) {
|
||||
$hasCleanupMethod = true;
|
||||
}
|
||||
|
||||
$whereClause = [
|
||||
'deleted' => 1,
|
||||
];
|
||||
@@ -722,7 +737,8 @@ class Cleanup implements JobDataLess
|
||||
$whereClause['createdAt<'] = $datetime->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$query = $this->entityManager->getQueryBuilder()
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->select()
|
||||
->from($scope)
|
||||
->withDeleted()
|
||||
@@ -735,9 +751,9 @@ class Cleanup implements JobDataLess
|
||||
->find();
|
||||
|
||||
foreach ($deletedEntityList as $entity) {
|
||||
if ($hasCleanupMethod) {
|
||||
if (method_exists($service, 'cleanup')) {
|
||||
try {
|
||||
$service->cleanup($entity->id);
|
||||
$service->cleanup($entity->getId());
|
||||
}
|
||||
catch (Throwable $e) {
|
||||
$this->log->error("Cleanup job: Cleanup scope {$scope}: " . $e->getMessage());
|
||||
|
||||
@@ -48,15 +48,15 @@ use Espo\{
|
||||
|
||||
class MassDelete implements MassAction
|
||||
{
|
||||
protected $massDeleteOriginal;
|
||||
private $massDeleteOriginal;
|
||||
|
||||
protected $queryBuilder;
|
||||
private $queryBuilder;
|
||||
|
||||
protected $entityManager;
|
||||
private $entityManager;
|
||||
|
||||
protected $acl;
|
||||
private $acl;
|
||||
|
||||
protected $user;
|
||||
private $user;
|
||||
|
||||
public function __construct(
|
||||
MassDeleteOriginal $massDeleteOriginal,
|
||||
@@ -87,14 +87,14 @@ class MassDelete implements MassAction
|
||||
$query = $this->queryBuilder->build($params);
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRDBRepository('User')
|
||||
->clone($query)
|
||||
->sth()
|
||||
->select(['id'])
|
||||
->find();
|
||||
|
||||
foreach ($collection as $entity) {
|
||||
$this->checkEntity($entity, $data);
|
||||
$this->checkEntity($entity);
|
||||
}
|
||||
|
||||
return $this->massDeleteOriginal->process($params, $data);
|
||||
@@ -102,11 +102,11 @@ class MassDelete implements MassAction
|
||||
|
||||
protected function checkEntity(Entity $entity): void
|
||||
{
|
||||
if ($entity->id === 'system') {
|
||||
if ($entity->getId() === 'system') {
|
||||
throw new Forbidden("Can't delete 'system' user.");
|
||||
}
|
||||
|
||||
if ($entity->id === $this->user->id) {
|
||||
if ($entity->getId() === $this->user->getId()) {
|
||||
throw new Forbidden("Can't delete own user.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,19 +50,19 @@ use Espo\{
|
||||
|
||||
class MassUpdate implements MassAction
|
||||
{
|
||||
protected $massUpdateOriginal;
|
||||
private $massUpdateOriginal;
|
||||
|
||||
protected $queryBuilder;
|
||||
private $queryBuilder;
|
||||
|
||||
protected $entityManager;
|
||||
private $entityManager;
|
||||
|
||||
protected $acl;
|
||||
private $acl;
|
||||
|
||||
protected $user;
|
||||
private $user;
|
||||
|
||||
protected $fileManager;
|
||||
private $fileManager;
|
||||
|
||||
protected $dataManager;
|
||||
private $dataManager;
|
||||
|
||||
public function __construct(
|
||||
MassUpdateOriginal $massUpdateOriginal,
|
||||
@@ -108,7 +108,7 @@ class MassUpdate implements MassAction
|
||||
$query = $this->queryBuilder->build($params);
|
||||
|
||||
$collection = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRDBRepository('User')
|
||||
->clone($query)
|
||||
->sth()
|
||||
->select(['id'])
|
||||
@@ -127,11 +127,11 @@ class MassUpdate implements MassAction
|
||||
|
||||
protected function checkEntity(Entity $entity, Data $data): void
|
||||
{
|
||||
if ($entity->id === 'system') {
|
||||
if ($entity->getId() === 'system') {
|
||||
throw new Forbidden("Can't update 'system' user.");
|
||||
}
|
||||
|
||||
if ($entity->id === $this->user->id) {
|
||||
if ($entity->getId() === $this->user->getId()) {
|
||||
if ($data->has('isActive')) {
|
||||
throw new Forbidden("Can't change 'isActive' field for own user.");
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @implements LinkHook<\Espo\Entities\Team>
|
||||
*/
|
||||
class BeforeLinkUserCheck implements LinkHook
|
||||
{
|
||||
public function process(Entity $entity, string $link, Entity $foreignEntity): void
|
||||
@@ -44,6 +47,8 @@ class BeforeLinkUserCheck implements LinkHook
|
||||
return;
|
||||
}
|
||||
|
||||
assert($foreignEntity instanceof User);
|
||||
|
||||
$this->processUserCheck($foreignEntity);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class EmailAddressHelper
|
||||
public function getEmailAddressIdByValue(string $value): ?string
|
||||
{
|
||||
$emailAddress = $this->entityManager
|
||||
->getRepository('EmailAddress')
|
||||
->getRDBRepository('EmailAddress')
|
||||
->where([
|
||||
'lower' => strtolower($value),
|
||||
])
|
||||
@@ -55,6 +55,6 @@ class EmailAddressHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
return $emailAddress->id;
|
||||
return $emailAddress->getId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,12 +44,16 @@ use Espo\{
|
||||
|
||||
class FromEquals implements ItemConverter
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* @var EmailAddressHelper
|
||||
*/
|
||||
protected $emailAddressHelper;
|
||||
|
||||
protected $randomStringGenerator;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
EmailAddressHelper $emailAddressHelper
|
||||
|
||||
@@ -45,10 +45,19 @@ use Espo\{
|
||||
|
||||
class InFolder implements ItemConverter
|
||||
{
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
/**
|
||||
* @var JoinHelper
|
||||
*/
|
||||
protected $joinHelper;
|
||||
|
||||
public function __construct(User $user, EntityManager $entityManager, JoinHelper $joinHelper)
|
||||
@@ -186,7 +195,7 @@ class InFolder implements ItemConverter
|
||||
protected function getEmailAddressIdList(): array
|
||||
{
|
||||
$emailAddressList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRDBRepository('User')
|
||||
->getRelation($this->user, 'emailAddresses')
|
||||
->select(['id'])
|
||||
->find();
|
||||
@@ -194,7 +203,7 @@ class InFolder implements ItemConverter
|
||||
$emailAddressIdList = [];
|
||||
|
||||
foreach ($emailAddressList as $emailAddress) {
|
||||
$emailAddressIdList[] = $emailAddress->id;
|
||||
$emailAddressIdList[] = $emailAddress->getId();
|
||||
}
|
||||
|
||||
return $emailAddressIdList;
|
||||
|
||||
@@ -36,19 +36,36 @@ class Orphan implements Filter
|
||||
{
|
||||
public function apply(SelectBuilder $queryBuilder): void
|
||||
{
|
||||
$queryBuilder->where([
|
||||
'entityEmailAddress.id' => null,
|
||||
]);
|
||||
|
||||
$queryBuilder->leftJoin(
|
||||
'EntityEmailAddress',
|
||||
'entityEmailAddress',
|
||||
[
|
||||
'emailAddressId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$queryBuilder->distinct();
|
||||
$queryBuilder
|
||||
->distinct()
|
||||
->leftJoin(
|
||||
'EntityEmailAddress',
|
||||
'entityEmailAddress',
|
||||
[
|
||||
'emailAddressId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
)
|
||||
->leftJoin(
|
||||
'EmailEmailAddress',
|
||||
'emailEmailAddress',
|
||||
[
|
||||
'emailAddressId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
)
|
||||
->leftJoin(
|
||||
'Email',
|
||||
'email',
|
||||
[
|
||||
'fromEmailAddressId:' => 'id',
|
||||
'deleted' => false,
|
||||
]
|
||||
)
|
||||
->where([
|
||||
'entityEmailAddress.id' => null,
|
||||
'emailEmailAddress.id' => null,
|
||||
'email.id' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,19 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Controllers\RecordBase;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class ActionHistoryRecord extends RecordBase
|
||||
{
|
||||
public function beforeUpdate(): void
|
||||
public function postActionCreate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeCreate(): void
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Services\Attachment as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Exceptions\Forbidden,
|
||||
Exceptions\BadRequest,
|
||||
@@ -37,18 +39,20 @@ use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Attachment extends RecordBase
|
||||
{
|
||||
public function beforeList(): void
|
||||
public function getActionList(Request $request, Response $response): stdClass
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return parent::getActionList($request, $response);
|
||||
}
|
||||
|
||||
public function postActionGetAttachmentFromImageUrl(Request $request): StdClass
|
||||
public function postActionGetAttachmentFromImageUrl(Request $request): stdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -57,13 +61,15 @@ class Attachment extends RecordBase
|
||||
}
|
||||
|
||||
if (empty($data->field)) {
|
||||
throw new BadRequest('postActionGetAttachmentFromImageUrl: No field specified');
|
||||
throw new BadRequest('postActionGetAttachmentFromImageUrl: No field specified.');
|
||||
}
|
||||
|
||||
return $this->getRecordService()->getAttachmentFromImageUrl($data)->getValueMap();
|
||||
return $this->getAttachmentService()
|
||||
->getAttachmentFromImageUrl($data)
|
||||
->getValueMap();
|
||||
}
|
||||
|
||||
public function postActionGetCopiedAttachment(Request $request): StdClass
|
||||
public function postActionGetCopiedAttachment(Request $request): stdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -72,10 +78,12 @@ class Attachment extends RecordBase
|
||||
}
|
||||
|
||||
if (empty($data->field)) {
|
||||
throw new BadRequest('postActionGetCopiedAttachment copy: No field specified');
|
||||
throw new BadRequest('postActionGetCopiedAttachment copy: No field specified.');
|
||||
}
|
||||
|
||||
return $this->getRecordService()->getCopiedAttachment($data)->getValueMap();
|
||||
return $this->getAttachmentService()
|
||||
->getCopiedAttachment($data)
|
||||
->getValueMap();
|
||||
}
|
||||
|
||||
public function getActionFile(Request $request, Response $response): void
|
||||
@@ -86,7 +94,7 @@ class Attachment extends RecordBase
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$fileData = $this->getRecordService()->getFileData($id);
|
||||
$fileData = $this->getAttachmentService()->getFileData($id);
|
||||
|
||||
$response
|
||||
->setHeader('Content-Type', $fileData->type)
|
||||
@@ -94,4 +102,10 @@ class Attachment extends RecordBase
|
||||
->setHeader('Content-Length', (string) $fileData->size)
|
||||
->setBody($fileData->stream);
|
||||
}
|
||||
|
||||
private function getAttachmentService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Core\Controllers\Record;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class AuthLogRecord extends Record
|
||||
{
|
||||
@@ -40,22 +43,22 @@ class AuthLogRecord extends Record
|
||||
return $this->user->isAdmin();
|
||||
}
|
||||
|
||||
public function beforeUpdate(): void
|
||||
public function postActionCreate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeCreate(): void
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeCreateLink(): void
|
||||
public function postActionCreateLink(Request $request): bool
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeRemoveLink(): void
|
||||
public function deleteActionRemoveLink(Request $request): bool
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Core\Controllers\Record;
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
class AuthToken extends Record
|
||||
{
|
||||
@@ -40,12 +40,12 @@ class AuthToken extends Record
|
||||
return $this->user->isAdmin();
|
||||
}
|
||||
|
||||
public function beforeCreateLink(): void
|
||||
public function postActionCreateLink(Request $request): bool
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeRemoveLink(): void
|
||||
public function deleteActionRemoveLink(Request $request): bool
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ class DashboardTemplate extends Record
|
||||
|
||||
private function getDashboardTemplateService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,8 +52,10 @@ class Email extends Record
|
||||
}
|
||||
|
||||
$id = $data->id;
|
||||
$parentType = $data->parentType ?? null;
|
||||
$field = $data->field ?? null;
|
||||
|
||||
return $this->getEmailService()->getCopiedAttachments($id);
|
||||
return $this->getEmailService()->getCopiedAttachments($id, $parentType, null, $field);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,11 +319,13 @@ class Email extends Record
|
||||
|
||||
private function getEmailService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
|
||||
private function getEmailTemplateService(): EmailTemplateService
|
||||
{
|
||||
/** @var EmailTemplateService */
|
||||
return $this->getServiceFactory()->create('EmailTemplate');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,20 @@ namespace Espo\Controllers;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Services\EmailAccount as Service;
|
||||
|
||||
use Espo\Core\Di\CryptAware;
|
||||
use Espo\Core\Di\CryptSetter;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Record,
|
||||
Api\Request,
|
||||
};
|
||||
|
||||
class EmailAccount extends Record
|
||||
class EmailAccount extends Record implements CryptAware
|
||||
{
|
||||
use CryptSetter;
|
||||
|
||||
protected function checkAccess(): bool
|
||||
{
|
||||
return $this->acl->check('EmailAccountScope');
|
||||
@@ -59,7 +66,7 @@ class EmailAccount extends Record
|
||||
'userId' => $data->userId ?? null,
|
||||
];
|
||||
|
||||
return $this->getRecordService()->getFolders($params);
|
||||
return $this->getEmailAccountService()->getFolders($params);
|
||||
}
|
||||
|
||||
public function postActionTestConnection(Request $request): bool
|
||||
@@ -69,24 +76,28 @@ class EmailAccount extends Record
|
||||
if (is_null($data->password)) {
|
||||
$emailAccount = $this->entityManager->getEntity('EmailAccount', $data->id);
|
||||
|
||||
if (!$emailAccount || !$emailAccount->id) {
|
||||
if (!$emailAccount || !$emailAccount->getId()) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (
|
||||
$emailAccount->get('assignedUserId') != $this->user->id &&
|
||||
$emailAccount->get('assignedUserId') !== $this->user->getId() &&
|
||||
!$this->user->isAdmin()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$data->password = $this->getContainer()
|
||||
->get('crypt')
|
||||
->decrypt($emailAccount->get('password'));
|
||||
$data->password = $this->crypt->decrypt($emailAccount->get('password'));
|
||||
}
|
||||
|
||||
$this->getRecordService()->testConnection(get_object_vars($data));
|
||||
$this->getEmailAccountService()->testConnection(get_object_vars($data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getEmailAccountService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Services\EmailAddress as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
Api\Request,
|
||||
@@ -58,6 +60,12 @@ class EmailAddress extends RecordBase
|
||||
|
||||
$onlyActual = $request->getQueryParam('onlyActual') === 'true';
|
||||
|
||||
return $this->getRecordService()->searchInAddressBook($q, $maxSize, $onlyActual);
|
||||
return $this->getEmailAddressService()->searchInAddressBook($q, $maxSize, $onlyActual);
|
||||
}
|
||||
|
||||
private function getEmailAddressService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Services\EmailFolder as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
Api\Request,
|
||||
@@ -46,7 +48,7 @@ class EmailFolder extends RecordBase
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getRecordService()->moveUp($data->id);
|
||||
$this->getEmailFolderService()->moveUp($data->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -59,13 +61,19 @@ class EmailFolder extends RecordBase
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getRecordService()->moveDown($data->id);
|
||||
$this->getEmailFolderService()->moveDown($data->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getActionListAll(): array
|
||||
{
|
||||
return $this->getRecordService()->listAll();
|
||||
return $this->getEmailFolderService()->listAll();
|
||||
}
|
||||
|
||||
private function getEmailFolderService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,20 +29,22 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Services\EmailTemplate as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Record,
|
||||
Api\Request,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class EmailTemplate extends Record
|
||||
{
|
||||
public function actionParse(Request $request): StdClass
|
||||
public function actionParse(Request $request): stdClass
|
||||
{
|
||||
$id = $request->getQueryParam('id');
|
||||
|
||||
return (object) $this->getRecordService()->parse(
|
||||
return (object) $this->getEmailTempalteService()->parse(
|
||||
$id,
|
||||
[
|
||||
'emailAddress' => $request->getQueryParam('emailAddress'),
|
||||
@@ -54,4 +56,10 @@ class EmailTemplate extends Record
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private function getEmailTempalteService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,19 +235,14 @@ class EntityManager
|
||||
'labelForeign',
|
||||
];
|
||||
|
||||
$additionalParamList = [];
|
||||
|
||||
$params = [];
|
||||
|
||||
foreach ($paramList as $item) {
|
||||
if (array_key_exists($item, $data)) {
|
||||
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($additionalParamList as $item) {
|
||||
$params[$item] = filter_var($data[$item], \FILTER_SANITIZE_STRING);
|
||||
}
|
||||
|
||||
if (array_key_exists('linkMultipleField', $data)) {
|
||||
$params['linkMultipleField'] = $data['linkMultipleField'];
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ use Espo\Core\{
|
||||
Api\Response,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Extension extends RecordBase
|
||||
{
|
||||
@@ -47,7 +47,7 @@ class Extension extends RecordBase
|
||||
return $this->user->isAdmin();
|
||||
}
|
||||
|
||||
public function postActionUpload(Request $request): StdClass
|
||||
public function postActionUpload(Request $request): stdClass
|
||||
{
|
||||
$body = $request->getBodyContents();
|
||||
|
||||
@@ -117,12 +117,12 @@ class Extension extends RecordBase
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforeCreate(): void
|
||||
public function postActionCreate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeUpdate(): void
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Services\ExternalAccount as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
Api\Request,
|
||||
@@ -38,7 +40,7 @@ use Espo\Core\{
|
||||
Record\ReadParams,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class ExternalAccount extends RecordBase
|
||||
{
|
||||
@@ -49,20 +51,22 @@ class ExternalAccount extends RecordBase
|
||||
return $this->acl->checkScope('ExternalAccount');
|
||||
}
|
||||
|
||||
public function getActionList(Request $request, Response $response): StdClass
|
||||
public function getActionList(Request $request, Response $response): stdClass
|
||||
{
|
||||
$integrations = $this->entityManager->getRepository('Integration')->find();
|
||||
$integrations = $this->entityManager
|
||||
->getRDBRepository('Integration')
|
||||
->find();
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($integrations as $entity) {
|
||||
if (
|
||||
$entity->get('enabled') &&
|
||||
$this->metadata->get('integrations.' . $entity->id .'.allowUserAccounts')
|
||||
$this->metadata->get('integrations.' . $entity->getId() .'.allowUserAccounts')
|
||||
) {
|
||||
|
||||
$userAccountAclScope = $this->metadata
|
||||
->get(['integrations', $entity->id, 'userAccountAclScope']);
|
||||
->get(['integrations', $entity->getId(), 'userAccountAclScope']);
|
||||
|
||||
if ($userAccountAclScope) {
|
||||
if (!$this->acl->checkScope($userAccountAclScope)) {
|
||||
@@ -81,7 +85,7 @@ class ExternalAccount extends RecordBase
|
||||
];
|
||||
}
|
||||
|
||||
public function getActionGetOAuth2Info(Request $request): ?StdClass
|
||||
public function getActionGetOAuth2Info(Request $request): ?stdClass
|
||||
{
|
||||
$id = $request->getQueryParam('id');
|
||||
|
||||
@@ -97,14 +101,14 @@ class ExternalAccount extends RecordBase
|
||||
return (object) [
|
||||
'clientId' => $entity->get('clientId'),
|
||||
'redirectUri' => $this->config->get('siteUrl') . '?entryPoint=oauthCallback',
|
||||
'isConnected' => $this->getRecordService()->ping($integration, $userId)
|
||||
'isConnected' => $this->getExternalAccount()->ping($integration, $userId)
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getActionRead(Request $request, Response $response): StdClass
|
||||
public function getActionRead(Request $request, Response $response): stdClass
|
||||
{
|
||||
$id = $request->getRouteParam('id');
|
||||
|
||||
@@ -113,7 +117,7 @@ class ExternalAccount extends RecordBase
|
||||
->getValueMap();
|
||||
}
|
||||
|
||||
public function putActionUpdate(Request $request, Response $response): StdClass
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
$id = $request->getRouteParam('id');
|
||||
|
||||
@@ -151,8 +155,12 @@ class ExternalAccount extends RecordBase
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$service = $this->getRecordService();
|
||||
return $this->getExternalAccount()->authorizationCode($integration, $userId, $code);
|
||||
}
|
||||
|
||||
return $service->authorizationCode($integration, $userId, $code);
|
||||
private function getExternalAccount(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Tools\Import\Params as ImportParams;
|
||||
use Espo\Tools\Import\Service as Service;
|
||||
@@ -143,23 +144,18 @@ class Import extends Record
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforePatch(): void
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new BadRequest();
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeUpdate(): void
|
||||
public function postActionCreateLink(Request $request): bool
|
||||
{
|
||||
throw new BadRequest();
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeCreateLink(): void
|
||||
public function deleteActionRemoveLink(Request $request): bool
|
||||
{
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
public function beforeRemoveLink(): void
|
||||
{
|
||||
throw new BadRequest();
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,13 +29,22 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Services\InboundEmail as Service;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use Espo\Core\Di\CryptAware;
|
||||
use Espo\Core\Di\CryptSetter;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Record,
|
||||
Api\Request,
|
||||
};
|
||||
|
||||
class InboundEmail extends Record
|
||||
class InboundEmail extends Record implements CryptAware
|
||||
{
|
||||
use CryptSetter;
|
||||
|
||||
protected function checkAccess(): bool
|
||||
{
|
||||
return $this->getUser()->isAdmin();
|
||||
@@ -54,7 +63,7 @@ class InboundEmail extends Record
|
||||
'id' => $data->id ?? null,
|
||||
];
|
||||
|
||||
return $this->getRecordService()->getFolders($params);
|
||||
return $this->getInboundEmailService()->getFolders($params);
|
||||
}
|
||||
|
||||
public function postActionTestConnection(Request $request): bool
|
||||
@@ -64,17 +73,21 @@ class InboundEmail extends Record
|
||||
if (is_null($data->password)) {
|
||||
$inboundEmail = $this->entityManager->getEntity('InboundEmail', $data->id);
|
||||
|
||||
if (!$inboundEmail || !$inboundEmail->id) {
|
||||
if (!$inboundEmail || !$inboundEmail->getId()) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
$data->password = $this->getContainer()
|
||||
->get('crypt')
|
||||
->decrypt($inboundEmail->get('password'));
|
||||
$data->password = $this->crypt->decrypt($inboundEmail->get('password'));
|
||||
}
|
||||
|
||||
$this->getRecordService()->testConnection(get_object_vars($data));
|
||||
$this->getInboundEmailService()->testConnection(get_object_vars($data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getInboundEmailService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,12 @@
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Api\Response;
|
||||
use Espo\Core\Controllers\RecordBase;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class Job extends RecordBase
|
||||
{
|
||||
protected function checkAccess(): bool
|
||||
@@ -40,12 +43,12 @@ class Job extends RecordBase
|
||||
return $this->user->isAdmin();
|
||||
}
|
||||
|
||||
public function beforeCreate(): void
|
||||
public function postActionCreate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
public function beforeUpdate(): void
|
||||
public function putActionUpdate(Request $request, Response $response): stdClass
|
||||
{
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ use Espo\Core\{
|
||||
Api\Response,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class LeadCapture extends Record
|
||||
{
|
||||
@@ -54,10 +54,6 @@ class LeadCapture extends Record
|
||||
throw new BadRequest('No API key provided.');
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
throw new BadRequest('No payload provided.');
|
||||
}
|
||||
|
||||
$allowOrigin = $this->config->get('leadCaptureAllowOrigin', '*');
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $allowOrigin);
|
||||
@@ -88,7 +84,7 @@ class LeadCapture extends Record
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionGenerateNewApiKey(Request $request): StdClass
|
||||
public function postActionGenerateNewApiKey(Request $request): stdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -112,6 +108,7 @@ class LeadCapture extends Record
|
||||
|
||||
private function getLeadCaptureService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->getRecordService();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Services\Notification as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\RecordBase,
|
||||
@@ -37,13 +37,13 @@ use Espo\Core\{
|
||||
Api\Response,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Notification extends RecordBase
|
||||
{
|
||||
public static $defaultAction = 'list';
|
||||
|
||||
public function getActionList(Request $request, Response $response): StdClass
|
||||
public function getActionList(Request $request, Response $response): stdClass
|
||||
{
|
||||
$userId = $this->user->getId();
|
||||
|
||||
@@ -52,7 +52,7 @@ class Notification extends RecordBase
|
||||
$offset = $searchParams->getOffset();
|
||||
$maxSize = $searchParams->getMaxSize();
|
||||
|
||||
$after = $request->get('after');
|
||||
$after = $request->getQueryParam('after');
|
||||
|
||||
$params = [
|
||||
'offset' => $offset,
|
||||
@@ -60,9 +60,7 @@ class Notification extends RecordBase
|
||||
'after' => $after,
|
||||
];
|
||||
|
||||
$recordCollection = $this->recordServiceContainer
|
||||
->get('Notification')
|
||||
->getList($userId, $params);
|
||||
$recordCollection = $this->getNotificationService()->getList($userId, $params);
|
||||
|
||||
return (object) [
|
||||
'total' => $recordCollection->getTotal(),
|
||||
@@ -74,15 +72,21 @@ class Notification extends RecordBase
|
||||
{
|
||||
$userId = $this->user->getId();
|
||||
|
||||
return $this->recordServiceContainer->get('Notification')->getNotReadCount($userId);
|
||||
return $this->getNotificationService()->getNotReadCount($userId);
|
||||
}
|
||||
|
||||
public function postActionMarkAllRead(Request $request): bool
|
||||
{
|
||||
$userId = $this->user->getId();
|
||||
|
||||
$this->recordServiceContainer->get('Notification')->markAllRead($userId);
|
||||
$this->getNotificationService()->markAllRead($userId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getNotificationService(): Service
|
||||
{
|
||||
/** @var Service */
|
||||
return $this->recordServiceContainer->get('Notification');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,10 +83,6 @@ class Preferences
|
||||
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
if (!$userId) {
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->service
|
||||
->update($userId, $data)
|
||||
->getValueMap();
|
||||
|
||||
@@ -66,9 +66,9 @@ class Stream
|
||||
$offset = $searchParams->getOffset();
|
||||
$maxSize = $searchParams->getMaxSize();
|
||||
|
||||
$after = $request->get('after');
|
||||
$filter = $request->get('filter');
|
||||
$skipOwn = $request->get('skipOwn') === 'true';
|
||||
$after = $request->getQueryParam('after');
|
||||
$filter = $request->getQueryParam('filter');
|
||||
$skipOwn = $request->getQueryParam('skipOwn') === 'true';
|
||||
|
||||
$result = $this->service->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
@@ -96,9 +96,8 @@ class Stream
|
||||
$offset = $searchParams->getOffset();
|
||||
$maxSize = $searchParams->getMaxSize();
|
||||
|
||||
$after = $request->get('after');
|
||||
|
||||
$where = $request->get('where');
|
||||
$after = $request->getQueryParam('after');
|
||||
$where = $request->getQueryParam('where');
|
||||
|
||||
$result = $this->service->find($scope, $id, [
|
||||
'offset' => $offset,
|
||||
|
||||
@@ -42,6 +42,8 @@ class TwoFactorEmail
|
||||
{
|
||||
private $service;
|
||||
|
||||
private $user;
|
||||
|
||||
public function __construct(Service $service, User $user)
|
||||
{
|
||||
$this->service = $service;
|
||||
|
||||
@@ -42,6 +42,8 @@ class TwoFactorSms
|
||||
{
|
||||
private $service;
|
||||
|
||||
private $user;
|
||||
|
||||
public function __construct(Service $service, User $user)
|
||||
{
|
||||
$this->service = $service;
|
||||
|
||||
@@ -34,6 +34,8 @@ use Espo\Core\Exceptions\NotFound;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Services\User as Service;
|
||||
|
||||
use Espo\Core\{
|
||||
Controllers\Record,
|
||||
Api\Request,
|
||||
@@ -41,11 +43,11 @@ use Espo\Core\{
|
||||
Select\Where\Item as WhereItem,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class User extends Record
|
||||
{
|
||||
public function getActionAcl(Request $request): StdClass
|
||||
public function getActionAcl(Request $request): stdClass
|
||||
{
|
||||
$userId = $request->getQueryParam('id');
|
||||
|
||||
@@ -53,7 +55,7 @@ class User extends Record
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (!$this->user->isAdmin() && $this->user->getId() != $userId) {
|
||||
if (!$this->user->isAdmin() && $this->user->getId() !== $userId) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
@@ -77,18 +79,17 @@ class User extends Record
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
$this->getService('User')
|
||||
->changePassword(
|
||||
$this->user->getId(),
|
||||
$data->password,
|
||||
true,
|
||||
$data->currentPassword
|
||||
);
|
||||
$this->getUserService()->changePassword(
|
||||
$this->user->getId(),
|
||||
$data->password,
|
||||
true,
|
||||
$data->currentPassword
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionChangePasswordByRequest(Request $request): StdClass
|
||||
public function postActionChangePasswordByRequest(Request $request): stdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -96,7 +97,7 @@ class User extends Record
|
||||
throw new BadRequest();
|
||||
}
|
||||
|
||||
return $this->getService('User')->changePasswordByRequest($data->requestId, $data->password);
|
||||
return $this->getUserService()->changePasswordByRequest($data->requestId, $data->password);
|
||||
}
|
||||
|
||||
public function postActionPasswordChangeRequest(Request $request): bool
|
||||
@@ -116,12 +117,12 @@ class User extends Record
|
||||
$url = $data->url;
|
||||
}
|
||||
|
||||
$this->getService('User')->passwordChangeRequest($userName, $emailAddress, $url);
|
||||
$this->getUserService()->passwordChangeRequest($userName, $emailAddress, $url);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionGenerateNewApiKey(Request $request): StdClass
|
||||
public function postActionGenerateNewApiKey(Request $request): stdClass
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
@@ -133,7 +134,7 @@ class User extends Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return $this->getRecordService()
|
||||
return $this->getUserService()
|
||||
->generateNewApiKeyForEntity($data->id)
|
||||
->getValueMap();
|
||||
}
|
||||
@@ -150,23 +151,27 @@ class User extends Record
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$this->getRecordService()->generateNewPasswordForUser($data->id);
|
||||
$this->getUserService()->generateNewPasswordForUser($data->id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function beforeCreateLink(): void
|
||||
public function postActionCreateLink(Request $request): bool
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return parent::postActionCreateLink($request);
|
||||
}
|
||||
|
||||
public function beforeRemoveLink(): void
|
||||
public function deleteActionRemoveLink(Request $request): bool
|
||||
{
|
||||
if (!$this->user->isAdmin()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
return parent::deleteActionRemoveLink($request);
|
||||
}
|
||||
|
||||
protected function fetchSearchParamsFromRequest(Request $request): SearchParams
|
||||
@@ -187,4 +192,9 @@ class User extends Record
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
private function getUserService(): Service
|
||||
{
|
||||
return $this->getServiceFactory()->create('User');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ class UserSecurity
|
||||
{
|
||||
private $service;
|
||||
|
||||
private $user;
|
||||
|
||||
public function __construct(Service $service, User $user)
|
||||
{
|
||||
$this->service = $service;
|
||||
|
||||
@@ -35,7 +35,7 @@ use Espo\Core\{
|
||||
Api\Response,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Webhook extends RecordBase
|
||||
{
|
||||
@@ -48,7 +48,7 @@ class Webhook extends RecordBase
|
||||
return true;
|
||||
}
|
||||
|
||||
public function postActionCreate(Request $request, Response $response): StdClass
|
||||
public function postActionCreate(Request $request, Response $response): stdClass
|
||||
{
|
||||
$result = parent::postActionCreate($request, $response);
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class Acl
|
||||
* whether a scope level is set to 'enabled'.
|
||||
*
|
||||
* @param string|Entity $subject An entity type or entity.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string|null $action Action to check. Constants are available in the `Table` class.
|
||||
*
|
||||
* @throws NotImplemented
|
||||
*/
|
||||
@@ -128,7 +128,7 @@ class Acl
|
||||
* The same as `check` but does not throw NotImplemented exception.
|
||||
*
|
||||
* @param string|Entity $subject An entity type or entity.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string|null $action Action to check. Constants are available in the `Table` class.
|
||||
*/
|
||||
public function tryCheck($subject, ?string $action = null): bool
|
||||
{
|
||||
@@ -150,7 +150,7 @@ class Acl
|
||||
* Check access to a specific entity.
|
||||
*
|
||||
* @param Entity $entity An entity to check.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string $action Action to check. Constants are available in the `Table` class.
|
||||
*/
|
||||
public function checkEntity(Entity $entity, string $action = Table::ACTION_READ): bool
|
||||
{
|
||||
@@ -216,8 +216,8 @@ class Acl
|
||||
/**
|
||||
* Get attributes forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements. *
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements. *
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenAttributeList(
|
||||
string $scope,
|
||||
@@ -232,8 +232,8 @@ class Acl
|
||||
/**
|
||||
* Get fields forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenFieldList(
|
||||
string $scope,
|
||||
@@ -248,8 +248,8 @@ class Acl
|
||||
/**
|
||||
* Get links forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenLinkList(
|
||||
string $scope,
|
||||
@@ -283,8 +283,8 @@ class Acl
|
||||
/**
|
||||
* Get a restricted field list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedFieldList(string $scope, $type): array
|
||||
{
|
||||
@@ -294,8 +294,8 @@ class Acl
|
||||
/**
|
||||
* Get a restricted attribute list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedAttributeList(string $scope, $type): array
|
||||
{
|
||||
@@ -305,8 +305,8 @@ class Acl
|
||||
/**
|
||||
* Get a restricted link list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedLinkList(string $scope, $type): array
|
||||
{
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
/**
|
||||
* @template TEntity of \Espo\ORM\Entity
|
||||
* @extends AccessEntityCreateChecker<TEntity>, AccessEntityCreateChecker<TEntity>, AccessEntityReadChecker<TEntity>,
|
||||
* AccessEntityEditChecker<TEntity>, AccessEntityDeleteChecker<TEntity>
|
||||
*/
|
||||
interface AccessEntityCREDChecker extends
|
||||
|
||||
AccessEntityCreateChecker,
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
/**
|
||||
* @template TEntity of \Espo\ORM\Entity
|
||||
* @extends AccessEntityCreateChecker<TEntity>, AccessEntityCreateChecker<TEntity>, AccessEntityReadChecker<TEntity>,
|
||||
* AccessEntityEditChecker<TEntity>, AccessEntityDeleteChecker<TEntity>, AccessEntityStreamChecker<TEntity>
|
||||
*/
|
||||
interface AccessEntityCREDSChecker extends
|
||||
|
||||
AccessEntityCreateChecker,
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @template TEntity of Entity
|
||||
*/
|
||||
interface AccessEntityCreateChecker extends AccessCreateChecker
|
||||
{
|
||||
/**
|
||||
* Check 'create' access for an entity.
|
||||
*
|
||||
* @phpstan-param TEntity $entity
|
||||
*/
|
||||
public function checkEntityCreate(User $user, Entity $entity, ScopeData $data): bool;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @template TEntity of Entity
|
||||
*/
|
||||
interface AccessEntityDeleteChecker extends AccessDeleteChecker
|
||||
{
|
||||
/**
|
||||
* Check 'delete' access for an entity.
|
||||
*
|
||||
* @phpstan-param TEntity $entity
|
||||
*/
|
||||
public function checkEntityDelete(User $user, Entity $entity, ScopeData $data): bool;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @template TEntity of Entity
|
||||
*/
|
||||
interface AccessEntityEditChecker extends AccessEditChecker
|
||||
{
|
||||
/**
|
||||
* Check 'edit' access for an entity.
|
||||
*
|
||||
* @phpstan-param TEntity $entity
|
||||
*/
|
||||
public function checkEntityEdit(User $user, Entity $entity, ScopeData $data): bool;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @template TEntity of Entity
|
||||
*/
|
||||
interface AccessEntityReadChecker extends AccessReadChecker
|
||||
{
|
||||
/**
|
||||
* Check 'read' access for entity.
|
||||
*
|
||||
* @phpstan-param TEntity $entity
|
||||
*/
|
||||
public function checkEntityRead(User $user, Entity $entity, ScopeData $data): bool;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
* @template TEntity of Entity
|
||||
*/
|
||||
interface AccessEntityStreamChecker extends AccessStreamChecker
|
||||
{
|
||||
/**
|
||||
* Check 'stream' access for an entity.
|
||||
*
|
||||
* @phpstan-param TEntity $entity
|
||||
*/
|
||||
public function checkEntityStream(User $user, Entity $entity, ScopeData $data): bool;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
namespace Espo\Core\Acl\AssignmentChecker;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\ClassFinder,
|
||||
Utils\Metadata,
|
||||
InjectableFactory,
|
||||
Acl\AssignmentChecker,
|
||||
@@ -42,18 +41,14 @@ class AssignmentCheckerFactory
|
||||
{
|
||||
private $defaultClassName = DefaultAssignmentChecker::class;
|
||||
|
||||
private $classFinder;
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
ClassFinder $classFinder,
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->classFinder = $classFinder;
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
|
||||
@@ -33,22 +33,16 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
Utils\Metadata,
|
||||
Acl\AssignmentChecker,
|
||||
};
|
||||
use Espo\Core\Acl\AssignmentChecker;
|
||||
|
||||
class AssignmentCheckerManager
|
||||
{
|
||||
private $checkerCache = [];
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $factory;
|
||||
|
||||
public function __construct(Metadata $metadata, AssignmentCheckerFactory $factory)
|
||||
public function __construct(AssignmentCheckerFactory $factory)
|
||||
{
|
||||
$this->metadata = $metadata;
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
|
||||
use Espo\Repositories\User as UserRepository;
|
||||
|
||||
use Espo\ORM\{
|
||||
Entity,
|
||||
EntityManager,
|
||||
@@ -155,9 +159,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
$teamIdList = $user->get(self::ATTR_TEAMS_IDS);
|
||||
|
||||
if (
|
||||
!$this->entityManager
|
||||
->getRepository('User')
|
||||
->checkBelongsToAnyOfTeams($assignedUserId, $teamIdList)
|
||||
!$this->getUserRepository()->checkBelongsToAnyOfTeams($assignedUserId, $teamIdList)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -166,6 +168,12 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
private function getUserRepository(): UserRepository
|
||||
{
|
||||
/** @var UserRepository */
|
||||
return $this->entityManager->getRepository('User');
|
||||
}
|
||||
|
||||
protected function isPermittedTeams(User $user, Entity $entity): bool
|
||||
{
|
||||
$assignmentPermission = $this->aclManager->getPermissionLevel($user, 'assignmentPermission');
|
||||
@@ -174,6 +182,10 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$entity->hasLinkMultipleField(self::FIELD_TEAMS)) {
|
||||
return true;
|
||||
}
|
||||
@@ -224,7 +236,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isPermittedTeamsEmpty(User $user, Entity $entity): bool
|
||||
private function isPermittedTeamsEmpty(User $user, CoreEntity $entity): bool
|
||||
{
|
||||
$assignmentPermission = $this->aclManager->getPermissionLevel($user, 'assignmentPermission');
|
||||
|
||||
@@ -250,6 +262,10 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
|
||||
protected function isPermittedAssignedUsers(User $user, Entity $entity): bool
|
||||
{
|
||||
if (!$entity instanceof CoreEntity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS)) {
|
||||
return true;
|
||||
}
|
||||
@@ -309,7 +325,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isPermittedAssignedUsersLevelNo(User $user, Entity $entity): bool
|
||||
private function isPermittedAssignedUsersLevelNo(User $user, CoreEntity $entity): bool
|
||||
{
|
||||
$userIdList = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
|
||||
|
||||
@@ -328,7 +344,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isPermittedAssignedUsersLevelTeam(User $user, Entity $entity): bool
|
||||
private function isPermittedAssignedUsersLevelTeam(User $user, CoreEntity $entity): bool
|
||||
{
|
||||
$userIdList = $entity->getLinkMultipleIdList(self::FIELD_ASSIGNED_USERS);
|
||||
|
||||
@@ -342,9 +358,7 @@ class DefaultAssignmentChecker implements AssignmentChecker
|
||||
}
|
||||
|
||||
if (
|
||||
!$this->entityManager
|
||||
->getRepository('User')
|
||||
->checkBelongsToAnyOfTeams($userId, $teamIdList)
|
||||
!$this->getUserRepository()->checkBelongsToAnyOfTeams($userId, $teamIdList)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Core\ORM\Entity as CoreEntity;
|
||||
use Espo\Entities\User;
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ class DefaultOwnershipChecker implements OwnershipOwnChecker, OwnershipTeamCheck
|
||||
}
|
||||
}
|
||||
|
||||
if ($entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS)) {
|
||||
if ($entity instanceof CoreEntity && $entity->hasLinkMultipleField(self::FIELD_ASSIGNED_USERS)) {
|
||||
if ($entity->hasLinkMultipleId(self::FIELD_ASSIGNED_USERS, $user->getId())) {
|
||||
return true;
|
||||
}
|
||||
@@ -78,6 +78,8 @@ class DefaultOwnershipChecker implements OwnershipOwnChecker, OwnershipTeamCheck
|
||||
|
||||
public function checkTeam(User $user, Entity $entity): bool
|
||||
{
|
||||
assert($entity instanceof CoreEntity);
|
||||
|
||||
$userTeamIdList = $user->getLinkMultipleIdList(self::FIELD_TEAMS);
|
||||
|
||||
if (
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
@@ -37,6 +37,9 @@ use RuntimeException;
|
||||
*/
|
||||
class FieldData
|
||||
{
|
||||
/**
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private $raw;
|
||||
|
||||
private $actionData = [];
|
||||
@@ -77,7 +80,7 @@ class FieldData
|
||||
/**
|
||||
* Create from a raw table value.
|
||||
*/
|
||||
public static function fromRaw(StdClass $raw): self
|
||||
public static function fromRaw(stdClass $raw): self
|
||||
{
|
||||
$obj = new self();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ use Espo\Core\{
|
||||
Utils\Config,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Lists of restricted fields can be obtained from here. Restricted fields
|
||||
@@ -104,7 +104,7 @@ class GlobalRestricton
|
||||
|
||||
$isFromCache = true;
|
||||
|
||||
if (!$this->data instanceof StdClass) {
|
||||
if (!$this->data instanceof stdClass) {
|
||||
$this->log->error("ACL GlobalRestricton: Bad data fetched from cache.");
|
||||
|
||||
$this->data = null;
|
||||
@@ -122,7 +122,7 @@ class GlobalRestricton
|
||||
|
||||
protected function storeCacheFile(): void
|
||||
{
|
||||
$this->dataCache->store($this->cacheKey, $this->data, true);
|
||||
$this->dataCache->store($this->cacheKey, $this->data);
|
||||
}
|
||||
|
||||
protected function buildData(): void
|
||||
|
||||
@@ -38,7 +38,7 @@ use Espo\Core\{
|
||||
Utils\ObjectUtil,
|
||||
};
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
@@ -100,7 +100,7 @@ class Map
|
||||
/**
|
||||
* Get raw data (for front-end).
|
||||
*/
|
||||
public function getData(): StdClass
|
||||
public function getData(): stdClass
|
||||
{
|
||||
return ObjectUtil::clone($this->data);
|
||||
}
|
||||
@@ -108,11 +108,11 @@ class Map
|
||||
/**
|
||||
* Get a list of forbidden attributes for a scope and action.
|
||||
*
|
||||
* @param $scope A scope.
|
||||
* @param $action An action.
|
||||
* @param $thresholdLevel An attribute will be treated as forbidden if the level is
|
||||
* @param string $scope A scope.
|
||||
* @param string $action An action.
|
||||
* @param string $thresholdLevel An attribute will be treated as forbidden if the level is
|
||||
* equal to or lower than the threshold.
|
||||
* @return array<int, string>
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenAttributeList(
|
||||
string $scope,
|
||||
@@ -180,11 +180,11 @@ class Map
|
||||
/**
|
||||
* Get a list of forbidden fields for a scope and action.
|
||||
*
|
||||
* @param $scope A scope.
|
||||
* @param $action An action.
|
||||
* @param $thresholdLevel An attribute will be treated as forbidden if the level is
|
||||
* @param string $scope A scope.
|
||||
* @param string $action An action.
|
||||
* @param string $thresholdLevel An attribute will be treated as forbidden if the level is
|
||||
* equal to or lower than the threshold.
|
||||
* @return array<int, string>
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenFieldList(
|
||||
string $scope,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
namespace Espo\Core\Acl;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
@@ -150,14 +150,16 @@ class ScopeData
|
||||
/**
|
||||
* Create from a raw table value.
|
||||
*
|
||||
* @param StdClass|bool $raw
|
||||
* @param stdClass|bool $raw
|
||||
* @return self
|
||||
*/
|
||||
public static function fromRaw($raw): self
|
||||
{
|
||||
/** @var mixed $raw */
|
||||
|
||||
$obj = new self();
|
||||
|
||||
if ($raw instanceof StdClass) {
|
||||
if ($raw instanceof stdClass) {
|
||||
$obj->isBoolean = false;
|
||||
|
||||
$obj->actionData = get_object_vars($raw);
|
||||
|
||||
@@ -31,10 +31,8 @@ namespace Espo\Core\Acl\Table;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Entities\{
|
||||
User,
|
||||
Role as RoleEntity,
|
||||
};
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Role as RoleEntity;
|
||||
|
||||
class DefaultRoleListProvider implements RoleListProvider
|
||||
{
|
||||
@@ -49,14 +47,15 @@ class DefaultRoleListProvider implements RoleListProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, Role>
|
||||
* @return Role[]
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$roleList = [];
|
||||
|
||||
/** @var iterable<RoleEntity> */
|
||||
$userRoleList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRDBRepository('User')
|
||||
->getRelation($this->user, 'roles')
|
||||
->find();
|
||||
|
||||
@@ -64,14 +63,16 @@ class DefaultRoleListProvider implements RoleListProvider
|
||||
$roleList[] = $role;
|
||||
}
|
||||
|
||||
/** @var iterable<\Espo\Entities\Team> */
|
||||
$teamList = $this->entityManager
|
||||
->getRepository('User')
|
||||
->getRDBRepository('User')
|
||||
->getRelation($this->user, 'teams')
|
||||
->find();
|
||||
|
||||
foreach ($teamList as $team) {
|
||||
/** @var iterable<RoleEntity> */
|
||||
$teamRoleList = $this->entityManager
|
||||
->getRepository('Team')
|
||||
->getRDBRepository('Team')
|
||||
->getRelation($team, 'roles')
|
||||
->find();
|
||||
|
||||
|
||||
@@ -196,6 +196,9 @@ class DefaultTable implements Table
|
||||
$aclTableList = [];
|
||||
$fieldTableList = [];
|
||||
|
||||
$aclTable = (object) [];
|
||||
$fieldTable = (object) [];
|
||||
|
||||
if (!$this->user->isAdmin()) {
|
||||
$roleList = $this->roleListProvider->get();
|
||||
|
||||
|
||||
@@ -33,8 +33,9 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
Acl,
|
||||
Acl\GlobalRestricton,
|
||||
Acl\OwnerUserFieldProvider,
|
||||
@@ -97,18 +98,39 @@ class AclManager
|
||||
Table::ACTION_STREAM => AccessStreamChecker::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var AccessCheckerFactory|\Espo\Core\Portal\Acl\AccessChecker\AccessCheckerFactory
|
||||
*/
|
||||
protected $accessCheckerFactory;
|
||||
|
||||
/**
|
||||
* @var OwnershipCheckerFactory|\Espo\Core\Portal\Acl\OwnershipChecker\OwnershipCheckerFactory
|
||||
*/
|
||||
protected $ownershipCheckerFactory;
|
||||
|
||||
protected $tableFactory;
|
||||
/**
|
||||
* @var TableFactory
|
||||
*/
|
||||
private $tableFactory;
|
||||
|
||||
protected $mapFactory;
|
||||
/**
|
||||
* @var MapFactory
|
||||
*/
|
||||
private $mapFactory;
|
||||
|
||||
/**
|
||||
* @var GlobalRestricton
|
||||
*/
|
||||
protected $globalRestricton;
|
||||
|
||||
/**
|
||||
* @var OwnerUserFieldProvider
|
||||
*/
|
||||
protected $ownerUserFieldProvider;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $entityManager;
|
||||
|
||||
public function __construct(
|
||||
@@ -255,7 +277,7 @@ class AclManager
|
||||
*
|
||||
* @param User $user A user to check for.
|
||||
* @param string|Entity $subject An entity type or entity.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string|null $action Action to check. Constants are available in the `Table` class.
|
||||
*
|
||||
* @throws NotImplemented
|
||||
*/
|
||||
@@ -265,6 +287,7 @@ class AclManager
|
||||
return $this->checkScope($user, $subject, $action);
|
||||
}
|
||||
|
||||
/** @var mixed */
|
||||
$entity = $subject;
|
||||
|
||||
if ($entity instanceof Entity) {
|
||||
@@ -281,7 +304,7 @@ class AclManager
|
||||
*
|
||||
* @param User $user A user to check for.
|
||||
* @param string|Entity $subject An entity type or entity.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string|null $action Action to check. Constants are available in the `Table` class.
|
||||
*/
|
||||
public function tryCheck(User $user, $subject, ?string $action = null): bool
|
||||
{
|
||||
@@ -298,7 +321,7 @@ class AclManager
|
||||
*
|
||||
* @param User $user A user to check for.
|
||||
* @param Entity $entity An entity to check.
|
||||
* @param string|null Action to check. Constants are available in the `Table` class.
|
||||
* @param string $action Action to check. Constants are available in the `Table` class.
|
||||
*
|
||||
* @throws NotImplemented
|
||||
*/
|
||||
@@ -478,8 +501,8 @@ class AclManager
|
||||
/**
|
||||
* Get attributes forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenAttributeList(
|
||||
User $user,
|
||||
@@ -506,8 +529,8 @@ class AclManager
|
||||
/**
|
||||
* Get fields forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenFieldList(
|
||||
User $user,
|
||||
@@ -534,8 +557,8 @@ class AclManager
|
||||
/**
|
||||
* Get links forbidden for a user.
|
||||
*
|
||||
* @param $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return array<int, string>
|
||||
* @param string $thresholdLevel Should not be used. Stands for possible future enhancements.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeForbiddenLinkList(
|
||||
User $user,
|
||||
@@ -581,11 +604,10 @@ class AclManager
|
||||
if ($permission === Table::LEVEL_TEAM) {
|
||||
$teamIdList = $user->getLinkMultipleIdList('teams');
|
||||
|
||||
if (
|
||||
!$this->entityManager
|
||||
->getRepository('User')
|
||||
->checkBelongsToAnyOfTeams($userId, $teamIdList)
|
||||
) {
|
||||
/** @var \Espo\Repositories\User $userRepository */
|
||||
$userRepository = $this->entityManager->getRepository('User');
|
||||
|
||||
if (!$userRepository->checkBelongsToAnyOfTeams($userId, $teamIdList)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -618,8 +640,8 @@ class AclManager
|
||||
/**
|
||||
* Get a restricted field list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedFieldList(string $scope, $type): array
|
||||
{
|
||||
@@ -644,8 +666,8 @@ class AclManager
|
||||
/**
|
||||
* Get a restricted attribute list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedAttributeList(string $scope, $type): array
|
||||
{
|
||||
@@ -670,8 +692,8 @@ class AclManager
|
||||
/**
|
||||
* Get a restricted link list for a specific scope by a restriction type.
|
||||
*
|
||||
* @param string|array<int, string> $type
|
||||
* @return array<int, string>
|
||||
* @param string|string[] $type
|
||||
* @return string[]
|
||||
*/
|
||||
public function getScopeRestrictedLinkList(string $scope, $type): array
|
||||
{
|
||||
|
||||
@@ -35,6 +35,8 @@ use Espo\ORM\Entity;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\AclManager;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
Portal\AclManager as PortalAclManager,
|
||||
|
||||
@@ -52,17 +52,17 @@ use Espo\{
|
||||
|
||||
class ConvertCurrency implements Action
|
||||
{
|
||||
protected $acl;
|
||||
private $acl;
|
||||
|
||||
protected $entityManager;
|
||||
private $entityManager;
|
||||
|
||||
protected $fieldUtil;
|
||||
private $fieldUtil;
|
||||
|
||||
protected $metadata;
|
||||
private $metadata;
|
||||
|
||||
protected $configDataProvider;
|
||||
private $configDataProvider;
|
||||
|
||||
protected $currencyConverter;
|
||||
private $currencyConverter;
|
||||
|
||||
public function __construct(
|
||||
Acl $acl,
|
||||
@@ -125,7 +125,10 @@ class ConvertCurrency implements Action
|
||||
}
|
||||
|
||||
protected function convertEntity(
|
||||
Entity $entity, array $fieldList, string $targetCurrency, CurrencyRates $rates
|
||||
Entity $entity,
|
||||
array $fieldList,
|
||||
string $targetCurrency,
|
||||
CurrencyRates $rates
|
||||
) {
|
||||
foreach ($fieldList as $field) {
|
||||
$amount = $entity->get($field);
|
||||
|
||||
@@ -207,6 +207,7 @@ class Merger
|
||||
{
|
||||
$list = [];
|
||||
|
||||
/** @var iterable<PhoneNumber> */
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepository($entity->getEntityType())
|
||||
->getRelation($entity, 'phoneNumbers')
|
||||
@@ -226,6 +227,7 @@ class Merger
|
||||
{
|
||||
$list = [];
|
||||
|
||||
/** @var iterable<EmailAddress> */
|
||||
$collection = $this->entityManager
|
||||
->getRDBRepository($entity->getEntityType())
|
||||
->getRelation($entity, 'emailAddresses')
|
||||
|
||||
@@ -31,21 +31,25 @@ namespace Espo\Core\Action;
|
||||
|
||||
use Espo\Core\Utils\ObjectUtil;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Data
|
||||
{
|
||||
private $data;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
$this->data = (object) [];
|
||||
}
|
||||
|
||||
public function getRaw(): StdClass
|
||||
public function getRaw(): stdClass
|
||||
{
|
||||
return ObjectUtil::clone($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an item value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $name)
|
||||
@@ -53,12 +57,15 @@ class Data
|
||||
return $this->getRaw()->$name ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has an item.
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return property_exists($this->data, $name);
|
||||
}
|
||||
|
||||
public static function fromRaw(StdClass $data): self
|
||||
public static function fromRaw(stdClass $data): self
|
||||
{
|
||||
$obj = new self();
|
||||
|
||||
@@ -66,4 +73,23 @@ class Data
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone with an item value.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function with(string $name, $value): self
|
||||
{
|
||||
$obj = clone $this;
|
||||
|
||||
$obj->data->$name = $value;
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->data = ObjectUtil::clone($this->data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ use Espo\Core\{
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
class Service
|
||||
{
|
||||
@@ -68,7 +68,7 @@ class Service
|
||||
* @throws BadRequest
|
||||
* @throws NotFound
|
||||
*/
|
||||
public function process(string $entityType, string $action, string $id, StdClass $data): Entity
|
||||
public function process(string $entityType, string $action, string $id, stdClass $data): Entity
|
||||
{
|
||||
if (!$this->acl->checkScope($entityType)) {
|
||||
throw new ForbiddenSilent();
|
||||
@@ -91,10 +91,6 @@ class Service
|
||||
|
||||
$entity = $service->read($id, ReadParams::create());
|
||||
|
||||
if (!$entity) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,13 @@ use Espo\Core\{
|
||||
Utils\Json,
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
Api\RequestWrapper,
|
||||
Exceptions\NotFound,
|
||||
};
|
||||
|
||||
use ReflectionClass;
|
||||
use StdClass;
|
||||
use ReflectionNamedType;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Creates controller instances and processes actions.
|
||||
@@ -103,7 +105,7 @@ class ActionProcessor
|
||||
|
||||
$data = $request->getBodyContents();
|
||||
|
||||
if ($data && $request->getContentType() === 'application/json') {
|
||||
if ($data && $this->getRequestContentType($request) === 'application/json') {
|
||||
$data = json_decode($data);
|
||||
}
|
||||
|
||||
@@ -137,7 +139,7 @@ class ActionProcessor
|
||||
is_float($result) ||
|
||||
is_array($result) ||
|
||||
is_bool($result) ||
|
||||
$result instanceof StdClass
|
||||
$result instanceof stdClass
|
||||
) {
|
||||
$responseContents = Json::encode($result);
|
||||
}
|
||||
@@ -161,7 +163,11 @@ class ActionProcessor
|
||||
|
||||
$type = $params[0]->getType();
|
||||
|
||||
if (!$type || $type->isBuiltin()) {
|
||||
if (
|
||||
!$type ||
|
||||
!$type instanceof ReflectionNamedType ||
|
||||
$type->isBuiltin()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,4 +204,13 @@ class ActionProcessor
|
||||
'name' => $name,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getRequestContentType(Request $request): ?string
|
||||
{
|
||||
if ($request instanceof RequestWrapper) {
|
||||
return $request->getContentType();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +218,7 @@ class Auth
|
||||
return;
|
||||
}
|
||||
|
||||
$response->setStatus(500);
|
||||
|
||||
$this->log->error("Auth: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
protected function handleUnauthorized(Response $response, bool $showDialog): void
|
||||
|
||||
@@ -29,12 +29,13 @@
|
||||
|
||||
namespace Espo\Core\Api;
|
||||
|
||||
use Espo\Core\Exceptions\HasBody;
|
||||
|
||||
use Espo\Core\{
|
||||
Api\Request,
|
||||
Api\Response,
|
||||
Exceptions\Conflict,
|
||||
Exceptions\Error,
|
||||
Utils\Log,
|
||||
Utils\Config,
|
||||
};
|
||||
|
||||
use Throwable;
|
||||
@@ -72,9 +73,12 @@ class ErrorOutput
|
||||
|
||||
private $log;
|
||||
|
||||
public function __construct(Log $log)
|
||||
private $config;
|
||||
|
||||
public function __construct(Log $log, Config $config)
|
||||
{
|
||||
$this->log = $log;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function process(
|
||||
@@ -105,7 +109,7 @@ class ErrorOutput
|
||||
bool $toPrintBody = false
|
||||
): void {
|
||||
|
||||
$message = $exception->getMessage() ?? '';
|
||||
$message = $exception->getMessage();
|
||||
$statusCode = $exception->getCode();
|
||||
|
||||
if ($route) {
|
||||
@@ -131,13 +135,14 @@ class ErrorOutput
|
||||
}
|
||||
|
||||
$logMessageItemList[] = $request->getMethod() . ' ' . $request->getResourcePath();
|
||||
|
||||
if ($messageLineFile) {
|
||||
$logMessageItemList[] = $messageLineFile;
|
||||
}
|
||||
$logMessageItemList[] = $messageLineFile;
|
||||
|
||||
$logMessage = "($statusCode) " . implode("; ", $logMessageItemList);
|
||||
|
||||
if ($this->toPrintTrace()) {
|
||||
$logMessage .= " :: " . $exception->getTraceAsString();
|
||||
}
|
||||
|
||||
$this->log->log($logLevel, $logMessage);
|
||||
|
||||
$toPrintBodyXStatusReason = !in_array(
|
||||
@@ -155,7 +160,7 @@ class ErrorOutput
|
||||
$response->setHeader('X-Status-Reason', $this->stripInvalidCharactersFromHeaderValue($message));
|
||||
}
|
||||
|
||||
if ($this->doesExceptionHaveBody($exception)) {
|
||||
if ($exception instanceof HasBody && $this->exceptionHasBody($exception)) {
|
||||
$response->writeBody($exception->getBody());
|
||||
|
||||
$toPrintBody = false;
|
||||
@@ -176,20 +181,13 @@ class ErrorOutput
|
||||
}
|
||||
}
|
||||
|
||||
private function doesExceptionHaveBody(Throwable $exception): bool
|
||||
private function exceptionHasBody(Throwable $exception): bool
|
||||
{
|
||||
if (
|
||||
!$exception instanceof Error &&
|
||||
!$exception instanceof Conflict
|
||||
) {
|
||||
if (!$exception instanceof HasBody) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$exceptionBody = null;
|
||||
|
||||
if (method_exists($exception, 'getBody')) {
|
||||
$exceptionBody = $exception->getBody();
|
||||
}
|
||||
$exceptionBody = $exception->getBody();
|
||||
|
||||
return $exceptionBody !== null;
|
||||
}
|
||||
@@ -227,7 +225,7 @@ class ErrorOutput
|
||||
{
|
||||
$requestBodyString = $this->clearPasswords($request->getBodyContents());
|
||||
|
||||
$message = $exception->getMessage() ?? '';
|
||||
$message = $exception->getMessage();
|
||||
$statusCode = $exception->getCode();
|
||||
|
||||
$routeParams = $request->getRouteParams();
|
||||
@@ -240,7 +238,7 @@ class ErrorOutput
|
||||
$logMessageItemList[] = $message;
|
||||
}
|
||||
|
||||
$logMessageItemList[] .= $request->getMethod() . ' ' . $request->getResourcePath();
|
||||
$logMessageItemList[] = $request->getMethod() . ' ' . $request->getResourcePath();
|
||||
|
||||
if ($requestBodyString) {
|
||||
$logMessageItemList[] = "Input data: " . $requestBodyString;
|
||||
@@ -256,4 +254,9 @@ class ErrorOutput
|
||||
|
||||
$this->log->log('debug', $logMessage);
|
||||
}
|
||||
|
||||
private function toPrintTrace(): bool
|
||||
{
|
||||
return (bool) $this->config->get('logger.printTrace');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Espo\Core\Api;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Representation of an HTTP request.
|
||||
@@ -46,7 +46,7 @@ interface Request
|
||||
/**
|
||||
* Get a query parameter.
|
||||
*
|
||||
* @return ?string|array
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getQueryParam(string $name);
|
||||
|
||||
@@ -110,7 +110,7 @@ interface Request
|
||||
/**
|
||||
* Get a parsed body. If JSON array is passed, then will be converted to `{"list": ARRAY}`.
|
||||
*/
|
||||
public function getParsedBody(): StdClass;
|
||||
public function getParsedBody(): stdClass;
|
||||
|
||||
/**
|
||||
* Get a cookie param value.
|
||||
|
||||
@@ -35,7 +35,7 @@ use Psr\Http\Message\UriInterface;
|
||||
|
||||
use Slim\Psr7\Factory\UriFactory;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* An empty stub for Request.
|
||||
@@ -48,7 +48,7 @@ class RequestNull implements ApiRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string|array
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getQueryParam(string $name)
|
||||
{
|
||||
@@ -113,7 +113,7 @@ class RequestNull implements ApiRequest
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getParsedBody(): StdClass
|
||||
public function getParsedBody(): stdClass
|
||||
{
|
||||
return (object) [];
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ use Psr\Http\Message\{
|
||||
|
||||
use Espo\Core\Api\Request as ApiRequest;
|
||||
|
||||
use StdClass;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Adapter for PSR-7 request interface.
|
||||
@@ -103,7 +103,7 @@ class RequestWrapper implements ApiRequest
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ?string|array
|
||||
* @return string|array|null
|
||||
*/
|
||||
public function getQueryParam(string $name)
|
||||
{
|
||||
@@ -175,7 +175,7 @@ class RequestWrapper implements ApiRequest
|
||||
return $contents;
|
||||
}
|
||||
|
||||
public function getParsedBody(): StdClass
|
||||
public function getParsedBody(): stdClass
|
||||
{
|
||||
if ($this->parsedBody === null) {
|
||||
$this->initParsedBody();
|
||||
|
||||
@@ -67,8 +67,8 @@ class Application
|
||||
/**
|
||||
* Run an application runner.
|
||||
*
|
||||
* @param $className A runner class name.
|
||||
* @param $params Runner parameters.
|
||||
* @param string $className A runner class name.
|
||||
* @param ?RunnerParams $params Runner parameters.
|
||||
*/
|
||||
public function run(string $className, ?RunnerParams $params = null): void
|
||||
{
|
||||
|
||||
@@ -77,7 +77,7 @@ class Daemon implements Runner
|
||||
|
||||
$processList = [];
|
||||
|
||||
while (true) {
|
||||
while (true) { /** @phpstan-ignore-line */
|
||||
$toSkip = false;
|
||||
$runningCount = 0;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace Espo\Core;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Entities\Portal as PortalEntity;
|
||||
use Espo\Entities\User as UserEntity;
|
||||
|
||||
|
||||
@@ -48,11 +48,12 @@ class EspoManager implements Manager
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
|
||||
$this->repository = $entityManager->getRepository(AuthTokenEntity::ENTITY_TYPE);
|
||||
$this->repository = $entityManager->getRDBRepository(AuthTokenEntity::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
public function get(string $token): ?AuthToken
|
||||
{
|
||||
/** @var ?AuthTokenEntity */
|
||||
$authToken = $this->entityManager
|
||||
->getRDBRepository(AuthTokenEntity::ENTITY_TYPE)
|
||||
->select([
|
||||
@@ -77,6 +78,7 @@ class EspoManager implements Manager
|
||||
|
||||
public function create(Data $data): AuthToken
|
||||
{
|
||||
/** @var ?AuthTokenEntity */
|
||||
$authToken = $this->repository->getNew();
|
||||
|
||||
$authToken->set([
|
||||
@@ -101,6 +103,8 @@ class EspoManager implements Manager
|
||||
|
||||
public function inactivate(AuthToken $authToken): void
|
||||
{
|
||||
assert($authToken instanceof AuthTokenEntity);
|
||||
|
||||
$this->validateNotChanged($authToken);
|
||||
|
||||
$authToken->set('isActive', false);
|
||||
@@ -110,6 +114,8 @@ class EspoManager implements Manager
|
||||
|
||||
public function renew(AuthToken $authToken): void
|
||||
{
|
||||
assert($authToken instanceof AuthTokenEntity);
|
||||
|
||||
$this->validateNotChanged($authToken);
|
||||
|
||||
if ($authToken->isNew()) {
|
||||
@@ -132,7 +138,7 @@ class EspoManager implements Manager
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateNotChanged(AuthToken $authToken): void
|
||||
protected function validateNotChanged(AuthTokenEntity $authToken): void
|
||||
{
|
||||
if (
|
||||
$authToken->isAttributeChanged('token') ||
|
||||
|
||||
@@ -80,12 +80,17 @@ class Authentication
|
||||
|
||||
private $configDataProvider;
|
||||
|
||||
/**
|
||||
* @var EntityManagerProxy
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
private $loginFactory;
|
||||
|
||||
private $twoFactorLoginFactory;
|
||||
|
||||
private $authTokenManager;
|
||||
|
||||
private $hookManager;
|
||||
|
||||
private $log;
|
||||
@@ -284,11 +289,17 @@ class Authentication
|
||||
$this->authTokenManager->renew($authToken);
|
||||
}
|
||||
|
||||
$authTokenId = null;
|
||||
|
||||
if (property_exists($authToken, 'id')) {
|
||||
$authTokenId = $authToken->id ?? null;
|
||||
}
|
||||
|
||||
$user->set('token', $authToken->getToken());
|
||||
$user->set('authTokenId', $authToken->id ?? null);
|
||||
$user->set('authTokenId', $authTokenId);
|
||||
|
||||
if ($authLogRecord) {
|
||||
$authLogRecord->set('authTokenId', $authToken->id ?? null);
|
||||
$authLogRecord->set('authTokenId', $authTokenId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,6 +355,7 @@ class Authentication
|
||||
private function processAuthTokenCheck(AuthToken $authToken): bool
|
||||
{
|
||||
if ($this->allowAnyAccess && $authToken->getPortalId() && !$this->isPortal()) {
|
||||
/** @var ?Portal */
|
||||
$portal = $this->entityManager->getEntity('Portal', $authToken->getPortalId());
|
||||
|
||||
if ($portal) {
|
||||
@@ -408,7 +420,7 @@ class Authentication
|
||||
|
||||
if ($this->isPortal()) {
|
||||
$isPortalRelatedToUser = $this->entityManager
|
||||
->getRepository('Portal')
|
||||
->getRDBRepository('Portal')
|
||||
->isRelated($this->getPortal(), 'users', $user);
|
||||
|
||||
if (!$isPortalRelatedToUser) {
|
||||
@@ -548,6 +560,7 @@ class Authentication
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var ?AuthLogRecord */
|
||||
$authLogRecord = $this->entityManager->getEntity('AuthLogRecord');
|
||||
|
||||
$requestUrl =
|
||||
@@ -650,6 +663,7 @@ class Authentication
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ class UserFinder
|
||||
|
||||
public function find(string $username, string $hash): ?User
|
||||
{
|
||||
/** @var ?User */
|
||||
$user = $this->entityManager
|
||||
->getRDBRepository(User::ENTITY_TYPE)
|
||||
->where([
|
||||
@@ -58,6 +59,7 @@ class UserFinder
|
||||
|
||||
public function findApiHmac(string $apiKey): ?User
|
||||
{
|
||||
/** @var ?User */
|
||||
$user = $this->entityManager
|
||||
->getRDBRepository(User::ENTITY_TYPE)
|
||||
->where([
|
||||
@@ -72,6 +74,7 @@ class UserFinder
|
||||
|
||||
public function findApiApiKey(string $apiKey): ?User
|
||||
{
|
||||
/** @var ?User */
|
||||
$user = $this->entityManager
|
||||
->getRDBRepository(User::ENTITY_TYPE)
|
||||
->where([
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Espo\Core\Authentication\Hook;
|
||||
|
||||
use Espo\Core\Authentication\AuthenticationData;
|
||||
use Espo\Core\Api\Request;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\ServiceUnavailable;
|
||||
|
||||
/**
|
||||
* Before logging in, before credentials are checked.
|
||||
|
||||
38
application/Espo/Core/Authentication/LDAP/ClientFactory.php
Normal file
38
application/Espo/Core/Authentication/LDAP/ClientFactory.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?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\Authentication\LDAP;
|
||||
|
||||
class ClientFactory
|
||||
{
|
||||
public function create(array $options): Client
|
||||
{
|
||||
return new Client($options);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,14 @@
|
||||
|
||||
namespace Espo\Core\Authentication\Logins;
|
||||
|
||||
use Espo\Core\FieldProcessing\Relation\LinkMultipleSaver;
|
||||
use Espo\Core\FieldProcessing\EmailAddress\Saver as EmailAddressSaver;
|
||||
use Espo\Core\FieldProcessing\PhoneNumber\Saver as PhoneNumberSaver;
|
||||
|
||||
use Espo\Core\FieldProcessing\Saver\Params as SaverParams;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\{
|
||||
ORM\EntityManager,
|
||||
Api\Request,
|
||||
@@ -40,7 +48,8 @@ use Espo\Core\{
|
||||
Authentication\Login\Data,
|
||||
Authentication\Result,
|
||||
Authentication\LDAP\Utils as LDAPUtils,
|
||||
Authentication\LDAP\Client as LDAPClient,
|
||||
Authentication\LDAP\Client as Client,
|
||||
Authentication\LDAP\ClientFactory as ClientFactory,
|
||||
Authentication\AuthToken\AuthToken,
|
||||
Authentication\Result\FailReason,
|
||||
};
|
||||
@@ -51,9 +60,7 @@ class LDAP implements Login
|
||||
{
|
||||
private $utils;
|
||||
|
||||
private $ldapClient;
|
||||
|
||||
private $baseLogin;
|
||||
private $client;
|
||||
|
||||
private $isPortal;
|
||||
|
||||
@@ -67,6 +74,16 @@ class LDAP implements Login
|
||||
|
||||
private $log;
|
||||
|
||||
private $baseLogin;
|
||||
|
||||
private $clientFactory;
|
||||
|
||||
private $linkMultipleSaver;
|
||||
|
||||
private $emailAddressSaver;
|
||||
|
||||
private $phoneNumberSaver;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
EntityManager $entityManager,
|
||||
@@ -74,6 +91,10 @@ class LDAP implements Login
|
||||
Language $defaultLanguage,
|
||||
Log $log,
|
||||
Espo $baseLogin,
|
||||
ClientFactory $clientFactyory,
|
||||
LinkMultipleSaver $linkMultipleSaver,
|
||||
EmailAddressSaver $emailAddressSaver,
|
||||
PhoneNumberSaver $phoneNumberSaver,
|
||||
bool $isPortal = false
|
||||
) {
|
||||
$this->config = $config;
|
||||
@@ -82,6 +103,10 @@ class LDAP implements Login
|
||||
$this->language = $defaultLanguage;
|
||||
$this->log = $log;
|
||||
$this->baseLogin = $baseLogin;
|
||||
$this->clientFactory = $clientFactyory;
|
||||
$this->linkMultipleSaver = $linkMultipleSaver;
|
||||
$this->emailAddressSaver = $emailAddressSaver;
|
||||
$this->phoneNumberSaver = $phoneNumberSaver;
|
||||
|
||||
$this->isPortal = $isPortal;
|
||||
|
||||
@@ -160,6 +185,8 @@ class LDAP implements Login
|
||||
$this->log->info('LDAP: Administrator [' . $username . '] was logged in by Espo method.');
|
||||
}
|
||||
|
||||
$userDn = null;
|
||||
|
||||
if (!isset($adminUser)) {
|
||||
try {
|
||||
$userDn = $this->findLdapUserDnByUsername($username);
|
||||
@@ -227,26 +254,26 @@ class LDAP implements Login
|
||||
return Result::success($user);
|
||||
}
|
||||
|
||||
private function getLdapClient()
|
||||
private function getLdapClient(): Client
|
||||
{
|
||||
if (!isset($this->ldapClient)) {
|
||||
if (!isset($this->client)) {
|
||||
$options = $this->utils->getLdapClientOptions();
|
||||
|
||||
try {
|
||||
$this->ldapClient = new LDAPClient($options);
|
||||
$this->client = $this->clientFactory->create($options);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$this->log->error('LDAP error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->ldapClient;
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login by authorization token.
|
||||
*/
|
||||
private function loginByToken($username, AuthToken $authToken = null)
|
||||
private function loginByToken($username, AuthToken $authToken = null): ?User
|
||||
{
|
||||
if (!isset($authToken)) {
|
||||
return null;
|
||||
@@ -256,6 +283,10 @@ class LDAP implements Login
|
||||
|
||||
$user = $this->entityManager->getEntity('User', $userId);
|
||||
|
||||
if (!$user) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokenUsername = $user->get('userName');
|
||||
|
||||
if (strtolower($username) != strtolower($tokenUsername)) {
|
||||
@@ -268,6 +299,7 @@ class LDAP implements Login
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var ?User */
|
||||
return $this->entityManager
|
||||
->getRDBRepository('User')
|
||||
->where([
|
||||
@@ -333,9 +365,23 @@ class LDAP implements Login
|
||||
$this->entityManager->saveEntity($user, [
|
||||
// Prevent `user` service being loaded by hooks.
|
||||
'skipHooks' => true,
|
||||
'keepNew' => true,
|
||||
]);
|
||||
|
||||
return $this->entityManager->getEntity('User', $user->id);
|
||||
$saverParams = SaverParams::create()
|
||||
->withRawOptions([
|
||||
'skipLinkMultipleHooks' => true,
|
||||
]);
|
||||
|
||||
$this->linkMultipleSaver->process($user, 'teams', $saverParams);
|
||||
$this->linkMultipleSaver->process($user, 'portalRoles', $saverParams);
|
||||
$this->emailAddressSaver->process($user, $saverParams);
|
||||
$this->phoneNumberSaver->process($user, $saverParams);
|
||||
|
||||
$user->setAsNotNew();
|
||||
$user->updateFetchedValues();
|
||||
|
||||
return $this->entityManager->getEntity('User', $user->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +404,7 @@ class LDAP implements Login
|
||||
'(' . $options['userNameAttribute'] . '=' . $username . ')' .
|
||||
$loginFilterString . ')';
|
||||
|
||||
$result = $ldapClient->search($searchString, null, LDAPClient::SEARCH_SCOPE_SUB);
|
||||
$result = $ldapClient->search($searchString, null, Client::SEARCH_SCOPE_SUB);
|
||||
|
||||
$this->log->debug('LDAP: user search string: "' . $searchString . '"');
|
||||
|
||||
|
||||
@@ -81,11 +81,6 @@ class Data
|
||||
return $this->loggedUser;
|
||||
}
|
||||
|
||||
public function getStatus(): string
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function getView(): ?string
|
||||
{
|
||||
return $this->view;
|
||||
|
||||
@@ -45,6 +45,9 @@ use Espo\Core\Api\Request;
|
||||
|
||||
class EmailLogin implements Login
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
private $util;
|
||||
@@ -100,6 +103,7 @@ class EmailLogin implements Login
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ class Util
|
||||
*/
|
||||
private const CODE_LIMIT_PERIOD = '10 minutes';
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
private $config;
|
||||
@@ -183,6 +186,7 @@ class Util
|
||||
|
||||
private function findCodeEntity(User $user): ?TwoFactorCode
|
||||
{
|
||||
/** @var ?TwoFactorCode */
|
||||
return $this->entityManager
|
||||
->getRDBRepository(TwoFactorCode::ENTITY_TYPE)
|
||||
->where([
|
||||
@@ -323,6 +327,7 @@ class Util
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@ use Espo\Core\Api\Request;
|
||||
|
||||
class SmsLogin implements Login
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
private $util;
|
||||
@@ -99,6 +102,7 @@ class SmsLogin implements Login
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ class Util
|
||||
*/
|
||||
private const CODE_LIMIT_PERIOD = '20 minutes';
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $entityManager;
|
||||
|
||||
private $config;
|
||||
@@ -178,6 +181,7 @@ class Util
|
||||
|
||||
private function findCodeEntity(User $user): ?TwoFactorCode
|
||||
{
|
||||
/** @var ?TwoFactorCode */
|
||||
return $this->entityManager
|
||||
->getRDBRepository(TwoFactorCode::ENTITY_TYPE)
|
||||
->where([
|
||||
@@ -312,6 +316,7 @@ class Util
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
/** @var UserDataRepository */
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user