Passed
Push — developer ( 7b6ddf...cfca6e )
by Radosław
17:02
created

Privilege   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 44
eloc 101
c 0
b 0
f 0
dl 0
loc 167
rs 8.8798

2 Methods

Rating   Name   Duplication   Size   Complexity  
A getParentCrmId() 0 16 4
F isPermitted() 0 117 40

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 5.0 (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
	/** Permissions based on user. */
21
	const USER_PERMISSIONS = 1;
22
	/** All records of account assigned directly to contact. */
23
	const ACCOUNTS_RELATED_RECORDS = 2;
24
	/** All related records of account assigned directly to contact and accounts lower in hierarchy. */
25
	const ACCOUNTS_RELATED_RECORDS_AND_LOWER_IN_HIERARCHY = 3;
26
	/** All related records of account assigned directly to contact and accounts from hierarchy. */
27
	const ACCOUNTS_RELATED_RECORDS_IN_HIERARCHY = 4;
28
	/** All related records directly to contact. */
29
	const CONTACT_RELATED_RECORDS = 5;
30
31
	/**
32
	 * Function to check permission for a Module/Action/Record.
33
	 *
34
	 * @param string   $moduleName
35
	 * @param string   $actionName
36
	 * @param bool|int $record
37
	 * @param mixed    $userId
38
	 *
39
	 * @throws \Api\Core\Exception
40
	 *
41
	 * @return bool
42
	 */
43
	public static function isPermitted(string $moduleName, $actionName = null, $record = false, $userId = false)
44
	{
45
		if (!$userId) {
46
			$user = \App\User::getCurrentUserModel();
47
		} else {
48
			$user = \App\User::getUserModel($userId);
49
		}
50
		$userId = $user->getId();
51
		if (empty($record) || !$user->has('permission_type')) {
52
			return \App\Privilege::checkPermission($moduleName, $actionName, $record, $userId);
53
		}
54
		$permissionType = $user->get('permission_type');
55
		switch ($permissionType) {
56
			case self::USER_PERMISSIONS:
57
				return \App\Privilege::checkPermission($moduleName, $actionName, $record, $userId);
58
			case self::CONTACT_RELATED_RECORDS:
59
				$parentRecordId = $user->get('permission_crmid');
60
				break;
61
			case self::ACCOUNTS_RELATED_RECORDS:
62
				$parentRecordId = \App\Record::getParentRecord($user->get('permission_crmid'));
63
				break;
64
			case self::ACCOUNTS_RELATED_RECORDS_AND_LOWER_IN_HIERARCHY:
65
			case self::ACCOUNTS_RELATED_RECORDS_IN_HIERARCHY:
66
				$parentRecordId = static::getParentCrmId($user);
67
				break;
68
			default:
69
				throw new \Api\Core\Exception('Invalid permissions ', 400);
70
		}
71
		if ('ModComments' !== $moduleName && !($permissionFieldInfo = \Api\Core\Module::getApiFieldPermission($moduleName, $user->get('permission_app')))) {
72
			\App\Privilege::$isPermittedLevel = 'FIELD_PERMISSION_NOT_EXISTS';
73
			return false;
74
		}
75
		if (!\App\Privilege::checkPermission($moduleName, $actionName, $record, $userId)) {
76
			return false;
77
		}
78
79
		if (0 === \App\ModuleHierarchy::getModuleLevel($moduleName) || (self::CONTACT_RELATED_RECORDS === $permissionType && 'Contacts' === $moduleName)) {
80
			$permission = $parentRecordId === $record;
81
			\App\Privilege::$isPermittedLevel = 'RECORD_HIERARCHY_LEVEL_' . ($permission ? 'YES' : 'NO');
82
			return $permission;
83
		}
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
		if (\in_array($moduleName, ['Products', 'Services'])) {
92
			\App\Privilege::$isPermittedLevel = $moduleName . '_SPECIAL_PERMISSION_YES';
93
			return true;
94
		}
95
96
		$parentModule = \App\Record::getType($parentRecordId) ?? '';
97
		$moduleModel = $recordModel->getModule();
98
		$fieldsForParent = $moduleModel->getReferenceFieldsForModule($parentModule);
99
		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...
100
			foreach ($fieldsForParent as $referenceField) {
101
				if ($recordModel->get($referenceField->getName()) === $parentRecordId) {
102
					\App\Privilege::$isPermittedLevel = 'RECORD_RELATED_YES';
103
					return true;
104
				}
105
			}
106
			\App\Privilege::$isPermittedLevel = 'RECORD_RELATED_NO';
107
			return false;
108
		}
109
110
		foreach (array_keys(\App\Relation::getByModule($parentModule, true, $moduleName)) as $relationId) {
111
			$relationModel = \Vtiger_Relation_Model::getInstanceById($relationId);
112
			$relationModel->set('parentRecord', \Vtiger_Record_Model::getInstanceById($parentRecordId, $parentModule));
113
			$queryGenerator = $relationModel->getQuery();
114
			$queryGenerator->permissions = false;
115
			$queryGenerator->clearFields()->setFields(['id'])->addCondition('id', $record, 'e');
116
			if ($queryGenerator->createQuery()->exists()) {
117
				\App\Privilege::$isPermittedLevel = $moduleName . '_RELATED_YES';
118
				return true;
119
			}
120
		}
121
122
		if ($fields = $moduleModel->getFieldsByReference()) {
123
			foreach ($fields as $fieldModel) {
124
				if (!$fieldModel->isActiveField() || $recordModel->isEmpty($fieldModel->getName())) {
125
					continue;
126
				}
127
				$relRecordId = $recordModel->get($fieldModel->getName());
128
				foreach ($fieldModel->getReferenceList() as $relModuleName) {
129
					if ('Users' === $relModuleName || $relModuleName === $parentModule || $relModuleName === $moduleName) {
130
						continue;
131
					}
132
					$relModuleModel = \Vtiger_Module_Model::getInstance($relModuleName);
133
					foreach ($relModuleModel->getReferenceFieldsForModule($parentModule) as $referenceField) {
134
						if (\App\Record::isExists($relRecordId, $relModuleName) && \App\Record::getType($relRecordId) === $relModuleName && \Vtiger_Record_Model::getInstanceById($relRecordId, $relModuleName)->get($referenceField->getName()) === $parentRecordId) {
135
							\App\Privilege::$isPermittedLevel = $moduleName . '_RELATED_SL_YES';
136
							return true;
137
						}
138
					}
139
				}
140
			}
141
		}
142
143
		if ('Documents' === $moduleName) {
144
			foreach (\Documents_Record_Model::getReferenceModuleByDocId($record) as $parentModuleName) {
145
				$relationListView = \Vtiger_RelationListView_Model::getInstance($recordModel, $parentModuleName);
146
				$relationListView->setFields([]);
147
				$relationListView->getQueryGenerator()->setFields(['id'])->setLimit(10)->permissions = false;
148
				$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

148
				$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...
149
				while ($id = $dataReader->readColumn(0)) {
150
					if (\App\Privilege::isPermitted($parentModuleName, 'DetailView', $id, $user->getId())) {
151
						\App\Privilege::$isPermittedLevel = "PERMISSION_{$parentModuleName}_YES-{$id}";
152
						return true;
153
					}
154
				}
155
			}
156
		}
157
158
		\App\Privilege::$isPermittedLevel = 'ALL_PERMISSION_NO';
159
		return false;
160
	}
161
162
	/**
163
	 * Gets parent ID.
164
	 *
165
	 * @param \App\User $user
166
	 *
167
	 * @return int
168
	 */
169
	public static function getParentCrmId(\App\User $user): int
170
	{
171
		$contactId = $user->get('permission_crmid');
172
		$parentApiId = \App\Record::getParentRecord($contactId);
173
		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

173
		if (($parentId = (int) \App\Request::/** @scrutinizer ignore-call */ _getHeader('x-parent-id')) && $parentApiId !== $parentId) {
Loading history...
174
			$hierarchy = new \Api\WebservicePremium\BaseModule\Hierarchy();
175
			$hierarchy->setAllUserData(['crmid' => $contactId, 'type' => $user->get('permission_type')]);
176
			$hierarchy->findId = $parentId;
177
			$hierarchy->moduleName = \App\Record::getType($parentApiId);
178
			$records = $hierarchy->get();
179
			if (isset($records[$parentId])) {
180
				return $parentId;
181
			}
182
			throw new \Api\Core\Exception('No permission to X-PARENT-ID', 403);
183
		}
184
		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...
185
	}
186
}
187