Passed
Push — developer ( 6b5868...bed0f9 )
by Radosław
22:42 queued 03:39
created

Privilege   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 41
eloc 96
dl 0
loc 163
rs 9.1199
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A getParentCrmId() 0 16 4
F isPermitted() 0 107 37

How to fix   Complexity   

Complex Class

Complex classes like Privilege often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Privilege, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Privilege file for client portal.
4
 *
5
 * @package API
6
 *
7
 * @copyright YetiForce S.A.
8
 * @license   YetiForce Public License 6.5 (licenses/LicenseEN.txt or yetiforce.com)
9
 * @author    Tomasz Kur <[email protected]>
10
 * @author    Radosław Skrzypczak <[email protected]>
11
 */
12
13
namespace Api\WebservicePremium;
14
15
/**
16
 * Class to check permission for client portal.
17
 */
18
class Privilege
19
{
20
	/**
21
	 * Permissions based on user.
22
	 */
23
	const USER_PERMISSIONS = 1;
24
	/**
25
	 * All records of account assigned directly to contact.
26
	 */
27
	const ACCOUNTS_RELATED_RECORDS = 2;
28
	/**
29
	 * All related records of account assigned directly to contact and accounts lower in hierarchy.
30
	 */
31
	const ACCOUNTS_RELATED_RECORDS_AND_LOWER_IN_HIERARCHY = 3;
32
	/**
33
	 * All related records of account assigned directly to contact and accounts from hierarchy.
34
	 */
35
	const ACCOUNTS_RELATED_RECORDS_IN_HIERARCHY = 4;
36
37
	/**
38
	 * Function to check permission for a Module/Action/Record.
39
	 *
40
	 * @param string   $moduleName
41
	 * @param string   $actionName
42
	 * @param bool|int $record
43
	 * @param mixed    $userId
44
	 *
45
	 * @throws \Api\Core\Exception
46
	 *
47
	 * @return bool
48
	 */
49
	public static function isPermitted(string $moduleName, $actionName = null, $record = false, $userId = false)
50
	{
51
		if (!$userId) {
52
			$user = \App\User::getCurrentUserModel();
53
		} else {
54
			$user = \App\User::getUserModel($userId);
55
		}
56
		$userId = $user->getId();
57
		if (empty($record) || !$user->has('permission_type')) {
58
			return \App\Privilege::checkPermission($moduleName, $actionName, $record, $userId);
59
		}
60
		switch ($user->get('permission_type')) {
61
			case self::USER_PERMISSIONS:
62
				return \App\Privilege::checkPermission($moduleName, $actionName, $record, $userId);
63
			case self::ACCOUNTS_RELATED_RECORDS:
64
				$parentRecordId = \App\Record::getParentRecord($user->get('permission_crmid'));
65
				break;
66
			case self::ACCOUNTS_RELATED_RECORDS_AND_LOWER_IN_HIERARCHY:
67
			case self::ACCOUNTS_RELATED_RECORDS_IN_HIERARCHY:
68
				$parentRecordId = static::getParentCrmId($user);
69
				break;
70
			default:
71
				throw new \Api\Core\Exception('Invalid permissions ', 400);
72
		}
73
		if ('ModComments' !== $moduleName && !($permissionFieldInfo = \Api\Core\Module::getApiFieldPermission($moduleName, $user->get('permission_app')))) {
74
			\App\Privilege::$isPermittedLevel = 'FIELD_PERMISSION_NOT_EXISTS';
75
			return false;
76
		}
77
		if (!\App\Privilege::checkPermission($moduleName, $actionName, $record, $userId)) {
78
			return false;
79
		}
80
		if (0 === \App\ModuleHierarchy::getModuleLevel($moduleName)) {
81
			$permission = $parentRecordId === $record;
82
			\App\Privilege::$isPermittedLevel = 'RECORD_HIERARCHY_LEVEL_' . ($permission ? 'YES' : 'NO');
83
			return $permission;
84
		}
85
		$recordModel = \Vtiger_Record_Model::getInstanceById($record, $moduleName);
86
		if ('ModComments' !== $moduleName && !$recordModel->get($permissionFieldInfo['fieldname'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $permissionFieldInfo does not seem to be defined for all execution paths leading up to this point.
Loading history...
87
			\App\Privilege::$isPermittedLevel = "FIELD_PERMISSION_NO {$permissionFieldInfo['fieldname']}: {$recordModel->get($permissionFieldInfo['fieldname'])}";
88
			return false;
89
		}
90
91
		$parentModule = \App\Record::getType($parentRecordId) ?? '';
92
		$moduleModel = $recordModel->getModule();
93
		if (\in_array($moduleName, ['Products', 'Services'])) {
94
			\App\Privilege::$isPermittedLevel = $moduleName . '_SPECIAL_PERMISSION_YES';
95
			return true;
96
		}
97
		$fieldsForParent = $moduleModel->getReferenceFieldsForModule($parentModule);
98
		if ($fieldsForParent) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fieldsForParent of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
99
			foreach ($fieldsForParent as $referenceField) {
100
				if ($recordModel->get($referenceField->getName()) === $parentRecordId) {
101
					\App\Privilege::$isPermittedLevel = 'RECORD_RELATED_YES';
102
					return true;
103
				}
104
			}
105
			\App\Privilege::$isPermittedLevel = 'RECORD_RELATED_NO';
106
			return false;
107
		}
108
		foreach (array_keys(\App\Relation::getByModule($parentModule, true, $moduleName)) as $relationId) {
109
			$relationModel = \Vtiger_Relation_Model::getInstanceById($relationId);
110
			$relationModel->set('parentRecord', \Vtiger_Record_Model::getInstanceById($parentRecordId, $parentModule));
111
			$queryGenerator = $relationModel->getQuery();
112
			$queryGenerator->permissions = false;
113
			$queryGenerator->clearFields()->setFields(['id'])->addCondition('id', $record, 'e');
114
			if ($queryGenerator->createQuery()->exists()) {
115
				\App\Privilege::$isPermittedLevel = $moduleName . '_RELATED_YES';
116
				return true;
117
			}
118
		}
119
		if ($fields = $moduleModel->getFieldsByReference()) {
120
			foreach ($fields as $fieldModel) {
121
				if (!$fieldModel->isActiveField() || $recordModel->isEmpty($fieldModel->getName())) {
122
					continue;
123
				}
124
				$relRecordId = $recordModel->get($fieldModel->getName());
125
				foreach ($fieldModel->getReferenceList() as $relModuleName) {
126
					if ('Users' === $relModuleName || $relModuleName === $parentModule || $relModuleName === $moduleName) {
127
						continue;
128
					}
129
					$relModuleModel = \Vtiger_Module_Model::getInstance($relModuleName);
130
					foreach ($relModuleModel->getReferenceFieldsForModule($parentModule) as $referenceField) {
131
						if (\App\Record::isExists($relRecordId, $relModuleName) && \App\Record::getType($relRecordId) === $relModuleName && \Vtiger_Record_Model::getInstanceById($relRecordId, $relModuleName)->get($referenceField->getName()) === $parentRecordId) {
132
							\App\Privilege::$isPermittedLevel = $moduleName . '_RELATED_SL_YES';
133
							return true;
134
						}
135
					}
136
				}
137
			}
138
		}
139
		if ('Documents' === $moduleName) {
140
			foreach (\Documents_Record_Model::getReferenceModuleByDocId($record) as $parentModuleName) {
141
				$relationListView = \Vtiger_RelationListView_Model::getInstance($recordModel, $parentModuleName);
142
				$relationListView->setFields([]);
143
				$relationListView->getQueryGenerator()->setFields(['id'])->setLimit(10)->permissions = false;
144
				$dataReader = $relationListView->getRelationQuery()->createCommand()->query();
0 ignored issues
show
Bug introduced by
The method createCommand() does not exist on App\QueryGenerator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
				$dataReader = $relationListView->getRelationQuery()->/** @scrutinizer ignore-call */ createCommand()->query();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
145
				while ($id = $dataReader->readColumn(0)) {
146
					if (\App\Privilege::isPermitted($parentModuleName, 'DetailView', $id, $user->getId())) {
147
						\App\Privilege::$isPermittedLevel = "PERMISSION_{$parentModuleName}_YES-{$id}";
148
						return true;
149
					}
150
				}
151
			}
152
		}
153
154
		\App\Privilege::$isPermittedLevel = 'ALL_PERMISSION_NO';
155
		return false;
156
	}
157
158
	/**
159
	 * Gets parent ID.
160
	 *
161
	 * @param \App\User $user
162
	 *
163
	 * @return int
164
	 */
165
	public static function getParentCrmId(\App\User $user): int
166
	{
167
		$contactId = $user->get('permission_crmid');
168
		$parentApiId = \App\Record::getParentRecord($contactId);
169
		if (($parentId = (int) \App\Request::_getHeader('x-parent-id')) && $parentApiId !== $parentId) {
0 ignored issues
show
Bug introduced by
The method _getHeader() does not exist on App\Request. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

169
		if (($parentId = (int) \App\Request::/** @scrutinizer ignore-call */ _getHeader('x-parent-id')) && $parentApiId !== $parentId) {
Loading history...
170
			$hierarchy = new \Api\WebservicePremium\BaseModule\Hierarchy();
171
			$hierarchy->setAllUserData(['crmid' => $contactId, 'type' => $user->get('permission_type')]);
172
			$hierarchy->findId = $parentId;
173
			$hierarchy->moduleName = \App\Record::getType($parentApiId);
174
			$records = $hierarchy->get();
175
			if (isset($records[$parentId])) {
176
				return $parentId;
177
			}
178
			throw new \Api\Core\Exception('No permission to X-PARENT-ID', 403);
179
		}
180
		return $parentApiId;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $parentApiId could return the type null which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
181
	}
182
}
183