From fbf98e97548fa4ef849ae79b5b34c8da54ee124c Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 9 Dec 2015 17:21:05 +0200 Subject: [PATCH] frontend client refactor --- application/Espo/Core/Acl.php | 23 ++- application/Espo/Core/Acl/Base.php | 20 ++- application/Espo/Core/AclManager.php | 47 +++--- frontend/client/src/acl-manager.js | 193 +++++++++++++++++++++++++ frontend/client/src/acl.js | 205 ++++++++------------------- frontend/client/src/app.js | 12 +- 6 files changed, 324 insertions(+), 176 deletions(-) create mode 100644 frontend/client/src/acl-manager.js diff --git a/application/Espo/Core/Acl.php b/application/Espo/Core/Acl.php index cec1d9ce08..b704690779 100644 --- a/application/Espo/Core/Acl.php +++ b/application/Espo/Core/Acl.php @@ -79,19 +79,34 @@ class Acl return $this->getAclManager()->checkReadOnlyOwn($this->getUser(), $scope); } - public function check($subject, $action = null, $isOwner = null, $inTeam = null) + public function check($subject, $action = null) { - return $this->getAclManager()->check($this->getUser(), $subject, $action, $isOwner, $inTeam) ; + return $this->getAclManager()->check($this->getUser(), $subject, $action) ; } - public function checkScope($scope, $action = null, $isOwner = null, $inTeam = null, $entity = null) + public function checkScope($scope, $action = null) { - return $this->getAclManager()->checkScope($this->getUser(), $scope, $action, $isOwner, $inTeam, $entity) ; + return $this->getAclManager()->checkScope($this->getUser(), $scope, $action) ; + } + + public function checkEntity(Entity $entity, $action) + { + return $this->getAclManager()->checkEntity($this->getUser(), $entity, $action); } public function checkUser($permission, User $entity) { return $this->getAclManager()->checkUser($this->getUser(), $permission, $entity); } + + public function checkIsOwner(Entity $entity) + { + return $this->getAclManager()->checkUser($this->getUser(), $entity); + } + + public function checkInTeam(Entity $entity) + { + return $this->getAclManager()->checkUser($this->getUser(), $entity); + } } diff --git a/application/Espo/Core/Acl/Base.php b/application/Espo/Core/Acl/Base.php index e1d2490d81..3f0678f2d4 100644 --- a/application/Espo/Core/Acl/Base.php +++ b/application/Espo/Core/Acl/Base.php @@ -42,6 +42,8 @@ class Base implements Injectable 'aclManager' ); + protected $scope; + protected $injections = array(); public function inject($name, $object) @@ -49,9 +51,10 @@ class Base implements Injectable $this->injections[$name] = $object; } - public function __construct() + public function __construct($scope) { $this->init(); + $this->scope = $scope; } protected function init() @@ -106,11 +109,18 @@ class Base implements Injectable public function checkEntity(User $user, Entity $entity, $data, $action) { - return $this->checkScope($user, $data, $entity->getEntityType(), $action, null, null, $entity); + if ($user->isAdmin()) { + return true; + } + return $this->checkScope($user, $data, $action, null, null, $entity); } - public function checkScope(User $user, $data, $scope, $action = null, $isOwner = null, $inTeam = null, Entity $entity = null) + public function checkScope(User $user, $data, $action = null, $isOwner = null, $inTeam = null, Entity $entity = null) { + if ($user->isAdmin()) { + return true; + } + if (is_null($data)) { return false; } @@ -205,6 +215,10 @@ class Base implements Injectable public function checkEntityDelete(User $user, Entity $entity, $data) { + if ($user->isAdmin()) { + return true; + } + $result = $this->checkEntity($user, $entity, $data, 'delete'); if (!$result) { if (is_array($data)) { diff --git a/application/Espo/Core/AclManager.php b/application/Espo/Core/AclManager.php index 310ccf2d2f..99a27c806d 100644 --- a/application/Espo/Core/AclManager.php +++ b/application/Espo/Core/AclManager.php @@ -75,7 +75,7 @@ class AclManager } if (class_exists($className)) { - $acl = new $className(); + $acl = new $className($scope); $dependencies = $acl->getDependencyList(); foreach ($dependencies as $name) { $acl->inject($name, $this->container->get($name)); @@ -143,25 +143,16 @@ class AclManager return $this->getImplementation($scope)->checkReadOnlyOwn($user, $data); } - public function check(User $user, $subject, $action = null, $isOwner = null, $inTeam = null) + public function check(User $user, $subject, $action = null) { if ($user->isAdmin()) { return true; } if (is_string($subject)) { - return $this->checkScope($user, $subject, $action, $isOwner, $inTeam); + return $this->checkScope($user, $subject, $action); } else { $entity = $subject; if ($entity instanceof Entity) { - $entityType = $entity->getEntityType(); - - $impl = $this->getImplementation($entityType); - $methodName = 'checkEntity' . ucfirst($action); - if (method_exists($impl, $methodName)) { - $data = $this->getTable($user)->getScopeData($entityType); - return $impl->$methodName($user, $entity, $data); - } - return $this->checkEntity($user, $entity, $action); } } @@ -169,20 +160,34 @@ class AclManager public function checkEntity(User $user, Entity $entity, $action) { - if ($user->isAdmin()) { - return true; + $scope = $entity->getEntityType(); + + $data = $this->getTable($user)->getScopeData($scope); + + $impl = $this->getImplementation($scope); + + $methodName = 'checkEntity' . ucfirst($action); + if (method_exists($impl, $methodName)) { + return $impl->$methodName($user, $entity, $data); } - $data = $this->getTable($user)->getScopeData($entity->getEntityType()); - return $this->getImplementation($entity->getEntityType())->checkEntity($user, $entity, $data, $action); + + return $impl->checkEntity($user, $entity, $data, $action); } - public function checkScope(User $user, $scope, $action = null, $isOwner = null, $inTeam = null, $entity = null) + public function checkIsOwner(User $user, Entity $entity) + { + return $this->getImplementation($entity->getEntityType())->checkIsOwner($user, $entity); + } + + public function checkInTeam(User $user, Entity $entity, $action) + { + return $this->getImplementation($entity->getEntityType())->checkInTeam($user, $entity); + } + + public function checkScope(User $user, $scope, $action = null) { - if ($user->isAdmin()) { - return true; - } $data = $this->getTable($user)->getScopeData($scope); - return $this->getImplementation($scope)->checkScope($user, $data, $scope, $action, $isOwner, $inTeam, $entity); + return $this->getImplementation($scope)->checkScope($user, $data, $action); } public function checkUser(User $user, $permission, User $entity) diff --git a/frontend/client/src/acl-manager.js b/frontend/client/src/acl-manager.js new file mode 100644 index 0000000000..32f6ab0136 --- /dev/null +++ b/frontend/client/src/acl-manager.js @@ -0,0 +1,193 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko + * Website: http://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. + ************************************************************************/ + + +/** * Example: + * Lead: { + * edit: 'own', + * read: 'team', + * delete: 'no', + * } + */ + +Espo.define('acl-manager', ['acl'], function (Acl) { + + var AclManager = function (user, implementationClassMap) { + this.data = { + table: {} + }; + this.user = user || null; + + this.implementationHash = {}; + + this.implementationClassMap = implementationClassMap || {}; + } + + _.extend(AclManager.prototype, { + + data: null, + + user: null, + + getImplementation: function (scope) { + if (!(scope in this.implementationHash)) { + var implementationClass = Acl; + if (scope in this.implementationClassMap) { + implementationClass = this.implementationClassMap[scope]; + } + var obj = new implementationClass(this.getUser(), scope); + this.implementationHash[scope] = obj; + } + return this.implementationHash[scope]; + }, + + getUser: function () { + return this.user; + }, + + set: function (data) { + data = data || {}; + this.data = data; + this.data.table = this.data.table || {}; + }, + + get: function (name) { + if (this.user.isAdmin()) { + return true; + } + return this.data[name] || null; + }, + + clear: function () { + this.data = { + table: {} + }; + }, + + checkScope: function (scope, action, precise) { + var data = (this.data.table || {})[scope]; + if (typeof data === 'undefined') { + data = null; + } + return this.getImplementation(scope).checkScope(data, action, precise); + }, + + checkModel: function (model, action, precise) { + var scope = model.name; + + // todo move this to custom acl + if (action == 'edit') { + if (!model.isEditable()) { + return false; + } + } + if (action == 'delete') { + if (!model.isRemovable()) { + return false; + } + } + if (action == 'edit') { + if (model.has('isEditable')) { + return model.get('isEditable'); + } + } + if (action == 'delete') { + if (model.has('isRemovable')) { + return model.get('isRemovable'); + } + } + + var data = (this.data.table || {})[scope]; + if (typeof data === 'undefined') { + data = null; + } + + var impl = this.getImplementation(scope); + + var methodName = 'checkModel' + Espo.Utils.upperCaseFirst(action); + if (methodName in impl) { + return impl.methodName(model, data, precise); + } + + return impl.checkModel(model, data, action, precise); + }, + + check: function (subject, action, precise) { + if (typeof subject === 'string') { + return this.checkScope(subject, action, precise); + } else { + return this.checkModel(subject, action, precise); + } + }, + + checkIsOwner: function (model) { + return this.getImplementation(model.name).checkIsOwner(model); + }, + + checkInTeam: function (model) { + return this.getImplementation(model.name).checkIsOwner(model); + }, + + checkAssignmentPermission: function (user) { + return this.checkPermission('assignmentPermission', user); + }, + + checkUserPermission: function (user) { + return this.checkPermission('userPermission', user); + }, + + checkPermission: function (permission, user) { + var result = false; + + if (this.getUser().isAdmin()) { + result = true; + } else { + if (this.get(permission) === 'no') { + if (user.id == this.getUser().id) { + result = true; + } + } else if (this.get(permission) === 'team') { + if (user.has('teamsIds')) { + user.get('teamsIds').forEach(function (id) { + if (~(this.getUser().get('teamsIds') || []).indexOf(id)) { + result = true; + } + }, this); + } + } else { + result = true; + } + } + return result; + } + + }); + + return AclManager; +}); + diff --git a/frontend/client/src/acl.js b/frontend/client/src/acl.js index 0f8ffaf422..31a3db30e6 100644 --- a/frontend/client/src/acl.js +++ b/frontend/client/src/acl.js @@ -37,131 +37,93 @@ Espo.define('acl', [], function () { - var Acl = function (user) { - this.data = { - table: {} - }; + var Acl = function (user, scope) { this.user = user || null; + this.scope = scope; } _.extend(Acl.prototype, { - data: null, - user: null, - set: function (data) { - data = data || {}; - this.data = data; - this.data.table = this.data.table || {}; + getUser: function () { + return this.user; }, - get: function (name) { - if (this.user.isAdmin()) { - return true; - } - return this.data[name] || null; - }, - - check: function (scope, action, isOwner, inTeam, precise) { - if (this.user.isAdmin()) { + checkScope: function (data, action, precise, isOwner, inTeam) { + if (this.getUser().isAdmin()) { return true; } - if (scope in this.data.table) { - if (this.data.table[scope] === false) { - return false; - } - if (this.data.table[scope] === true) { - return true; - } - if (typeof this.data.table[scope] === 'string') { - return true; - } + if (data === false) { + return false; + } + if (data === true) { + return true; + } + if (typeof data === 'string') { + return true; + } + if (data === null) { + return true; + } - if (typeof action !== 'undefined') { - if (action in this.data.table[scope]) { - var value = this.data.table[scope][action]; + action = action || null; - if (value === 'all' || value === true) { - return true; - } + if (action !== null) { + if (action in data) { + var value = data[action]; - if (action != 'delete' && (value == 'no' || value === false)) { - return false; - } - - if (typeof isOwner === 'undefined') { - return true; - } - - if (isOwner && action == 'delete' && value === 'no') { - return this.check(scope, 'edit', isOwner); - } - - if (!value || value === 'no') { - return false; - } - - if (isOwner) { - if (value === 'own' || value === 'team') { - return true; - } - } - - if (value === 'team') { - if (inTeam === null) { - if (precise) { - return null; - } else { - return true; - } - } else { - return inTeam; - } - } + if (value === 'all' || value === true) { + return true; + } + if (action != 'delete' && (value == 'no' || value === false)) { return false; } + + if (typeof isOwner === 'undefined') { + return true; + } + + if (isOwner && action == 'delete' && value === 'no') { + return this.checkScope(data, 'edit', precise, isOwner); + } + + if (!value || value === 'no') { + return false; + } + + if (isOwner) { + if (value === 'own' || value === 'team') { + return true; + } + } + + if (value === 'team') { + if (inTeam === null) { + if (precise) { + return null; + } else { + return true; + } + } else { + return inTeam; + } + } + + return false; } - return true; } return true; }, - checkScope: function (scope, action) { - return this.check(scope, action); - }, - - checkModel: function (model, action, precise) { - if (action == 'edit') { - if (!model.isEditable()) { - return false; - } - } - if (action == 'delete') { - if (!model.isRemovable()) { - return false; - } - } - if (this.user.isAdmin()) { - return true; - } - if (action == 'edit') { - if (model.has('isEditable')) { - return model.get('isEditable'); - } - } - if (action == 'delete') { - if (model.has('isRemovable')) { - return model.get('isRemovable'); - } - } - return this.check(model.name, action, this.checkIsOwner(model), this.checkInTeam(model), precise); + checkModel: function (model, data, action, precise) { + return this.checkScope(data, action, precise, this.checkIsOwner(model), this.checkInTeam(model)); }, checkIsOwner: function (model) { - var result = this.user.id === model.get('assignedUserId') || this.user.id === model.get('createdById'); + var result = this.getUser().id === model.get('assignedUserId') || this.getUser().id === model.get('createdById'); if (!result) { if (!model.hasField('assignedUser') && !model.hasField('createdBy')) { return true; @@ -171,9 +133,9 @@ Espo.define('acl', [], function () { }, checkInTeam: function (model) { - var userTeamIdList = this.user.getTeamIdList(); + var userTeamIdList = this.getUser().getTeamIdList(); if (model.name == 'Team') { - return (userTeamIds.indexOf(model.id) != -1); + return (userTeamIdList.indexOf(model.id) != -1); } else { if (!model.has('teamsIds')) { return null; @@ -188,50 +150,9 @@ Espo.define('acl', [], function () { return inTeam; } return false; - }, - - clear: function () { - this.data = { - table: {} - }; - }, - - checkAssignmentPermission: function (user) { - return this.checkPermission('assignmentPermission', user); - }, - - checkUserPermission: function (user) { - return this.checkPermission('userPermission', user); - }, - - checkPermission: function (permission, user) { - var result = false; - - if (this.user.isAdmin()) { - result = true; - } else { - if (this.get(permission) === 'no') { - if (user.id == this.user.id) { - result = true; - } - } else if (this.get(permission) === 'team') { - if (user.has('teamsIds')) { - user.get('teamsIds').forEach(function (id) { - if (~(this.user.get('teamsIds') || []).indexOf(id)) { - result = true; - } - }, this); - } - } else { - result = true; - } - } - return result; - } }); return Acl; - }); diff --git a/frontend/client/src/app.js b/frontend/client/src/app.js index c85ea22375..0f064b4b5b 100644 --- a/frontend/client/src/app.js +++ b/frontend/client/src/app.js @@ -29,8 +29,8 @@ Espo.define( 'app', - ['ui', 'utils', 'acl', 'cache', 'storage', 'models/settings', 'language', 'metadata', 'field-manager', 'models/user', 'models/preferences', 'model-factory' ,'collection-factory', 'pre-loader', 'view-helper', 'controllers/base', 'router', 'date-time', 'layout-manager', 'theme-manager'], - function (Ui, Utils, Acl, Cache, Storage, Settings, Language, Metadata, FieldManager, User, Preferences, ModelFactory, CollectionFactory, PreLoader, ViewHelper, BaseController, Router, DateTime, LayoutManager, ThemeManager) { + ['ui', 'utils', 'acl-manager', 'cache', 'storage', 'models/settings', 'language', 'metadata', 'field-manager', 'models/user', 'models/preferences', 'model-factory' ,'collection-factory', 'pre-loader', 'view-helper', 'controllers/base', 'router', 'date-time', 'layout-manager', 'theme-manager'], + function (Ui, Utils, AclManager, Cache, Storage, Settings, Language, Metadata, FieldManager, User, Preferences, ModelFactory, CollectionFactory, PreLoader, ViewHelper, BaseController, Router, DateTime, LayoutManager, ThemeManager) { var App = function (options, callback) { var options = options || {}; @@ -66,7 +66,7 @@ Espo.define( this.user = new User(); this.preferences = new Preferences(); this.preferences.settings = this.settings; - this.acl = new Acl(this.user); + this.acl = new AclManager(this.user); this.themeManager = new ThemeManager(this.settings, this.preferences, this.metadata); @@ -184,10 +184,10 @@ Espo.define( this.trigger('action', params); this.getController(params.controller, function (controller) { - try { + //try { controller.doAction(params.action, params.options); this.trigger('action:done'); - } catch (e) { + /*} catch (e) { switch (e.name) { case 'AccessDenied': this.baseController.error403(); @@ -198,7 +198,7 @@ Espo.define( default: throw e; } - } + }*/ }.bind(this)); },