Passed
Push — master ( 591d77...ff0d74 )
by Marcel
04:58
created

ShareService   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 316
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 1
Metric Value
eloc 131
dl 0
loc 316
rs 8.96
c 8
b 0
f 1
wmc 43

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 1
A deleteByUser() 0 2 1
A create() 0 19 5
A getReportByToken() 0 3 1
A generateToken() 0 3 1
A deleteSharesByItem() 0 2 1
A verifyPassword() 0 2 1
C getSharedItems() 0 58 12
A read() 0 15 4
B getSharedPanoramaReport() 0 30 8
A update() 0 11 5
A delete() 0 3 1
A getSharedReport() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like ShareService 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 ShareService, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Analytics
4
 *
5
 * SPDX-FileCopyrightText: 2019-2022 Marcel Scherello
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8
9
namespace OCA\Analytics\Service;
10
11
use OCA\Analytics\Activity\ActivityManager;
12
use OCA\Analytics\Db\ShareMapper;
13
use OCA\Analytics\Db\ReportMapper;
14
use OCP\DB\Exception;
0 ignored issues
show
Bug introduced by
The type OCP\DB\Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use OCP\IGroupManager;
0 ignored issues
show
Bug introduced by
The type OCP\IGroupManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use OCP\IUserManager;
0 ignored issues
show
Bug introduced by
The type OCP\IUserManager was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use OCP\IUserSession;
0 ignored issues
show
Bug introduced by
The type OCP\IUserSession was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use OCP\Security\ISecureRandom;
0 ignored issues
show
Bug introduced by
The type OCP\Security\ISecureRandom was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use Psr\Log\LoggerInterface;
0 ignored issues
show
Bug introduced by
The type Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
21
class ShareService {
22
	const SHARE_TYPE_USER = 0;
23
	const SHARE_TYPE_GROUP = 1;
24
	const SHARE_TYPE_USERGROUP = 2; // obsolete
25
	const SHARE_TYPE_LINK = 3;
26
	const SHARE_TYPE_ROOM = 10;
27
	const SHARE_ITEM_TYPE_REPORT = 'report';
28
	const SHARE_ITEM_TYPE_PANORAMA = 'panorama';
29
	const SHARE_ITEM_TYPE_DATASET = 'dataset';
30
31
	/** @var IUserSession */
32
	private $userSession;
33
	/** @var LoggerInterface */
34
	private $logger;
35
	/** @var ShareMapper */
36
	private $ShareMapper;
37
	private $ReportMapper;
38
	private $secureRandom;
39
	private $ActivityManager;
40
	/** @var IGroupManager */
41
	private $groupManager;
42
	/** @var IUserManager */
43
	private $userManager;
44
	private $VariableService;
45
46
	public function __construct(
47
		IUserSession    $userSession,
48
		LoggerInterface $logger,
49
		ShareMapper     $ShareMapper,
50
		ReportMapper    $ReportMapper,
51
		ActivityManager $ActivityManager,
52
		IGroupManager   $groupManager,
53
		ISecureRandom   $secureRandom,
54
		IUserManager    $userManager,
55
		VariableService $VariableService
56
	) {
57
		$this->userSession = $userSession;
58
		$this->logger = $logger;
59
		$this->ShareMapper = $ShareMapper;
60
		$this->ReportMapper = $ReportMapper;
61
		$this->secureRandom = $secureRandom;
62
		$this->groupManager = $groupManager;
63
		$this->ActivityManager = $ActivityManager;
64
		$this->userManager = $userManager;
65
		$this->VariableService = $VariableService;
66
	}
67
68
	/**
69
	 * create a new share
70
	 *
71
	 * @param $item_type
72
	 * @param $item_source
73
	 * @param $type
74
	 * @param $user
75
	 * @return bool
76
	 * @throws Exception
77
	 */
78
	public function create($item_type, $item_source, $type, $user) {
79
		$token = null;
80
		if ((int)$type === self::SHARE_TYPE_LINK) {
81
			$token = $this->generateToken();
82
		}
83
		$this->ShareMapper->createShare($item_type, $item_source, $type, $user, $token);
84
85
		switch ($item_type) {
86
			case ShareService::SHARE_ITEM_TYPE_REPORT:
87
				$this->ActivityManager->triggerEvent($item_source, ActivityManager::OBJECT_REPORT, ActivityManager::SUBJECT_REPORT_SHARE);
88
				break;
89
			case ShareService::SHARE_ITEM_TYPE_DATASET:
90
				$this->ActivityManager->triggerEvent($item_source, ActivityManager::OBJECT_DATASET, ActivityManager::SUBJECT_DATASET_SHARE);
91
				break;
92
			case ShareService::SHARE_ITEM_TYPE_PANORAMA:
93
				$this->ActivityManager->triggerEvent($item_source, ActivityManager::OBJECT_PANORAMA, ActivityManager::SUBJECT_PANORAMA_SHARE);
94
				break;
95
		}
96
		return true;
97
	}
98
99
	/**
100
	 * get all shares for a report or panorama
101
	 *
102
	 * @param $item_source
103
	 * @param $item_type
104
	 * @return array
105
	 * @throws Exception
106
	 */
107
	public function read($item_type, $item_source) {
108
109
		$shares = $this->ShareMapper->getShares($item_type, $item_source);
110
		foreach ($shares as $key => $share) {
111
			if ((int)$share['type'] === self::SHARE_TYPE_USER) {
112
				if (!$this->userManager->userExists($share['uid_owner'])) {
113
					$this->ShareMapper->deleteShare($share['id']);
114
					unset($shares[$key]);
115
					continue;
116
				}
117
				$shares[$key]['displayName'] = $this->userManager->get($share['uid_owner'])->getDisplayName();
118
			}
119
			$shares[$key]['pass'] = $share['pass'] !== null;
120
		}
121
		return $shares;
122
	}
123
124
	/**
125
	 * get all report by token
126
	 *
127
	 * @param $token
128
	 * @return array
129
	 */
130
	public function getReportByToken($token) {
131
		$reportId = $this->ShareMapper->getReportByToken($token);
132
		return $this->VariableService->replaceTextVariables($reportId);
133
	}
134
135
	/**
136
	 * verify password hahes
137
	 *
138
	 * @param $password
139
	 * @param $sharePassword
140
	 * @return bool
141
	 */
142
	public function verifyPassword($password, $sharePassword) {
143
		return password_verify($password, $sharePassword);
144
	}
145
146
	/**
147
	 * get all shares for an item (report or panorama)
148
	 *
149
	 * @param $item_type
150
	 * @return array|false
151
	 * @throws Exception
152
	 */
153
	public function getSharedItems($item_type) {
154
		if ($item_type === self::SHARE_ITEM_TYPE_PANORAMA) {
155
			$sharedReports = $this->ShareMapper->getAllSharedPanoramas();
156
		} else {
157
			$sharedReports = $this->ShareMapper->getAllSharedReports();
158
		}
159
		$groupsOfUser = $this->groupManager->getUserGroups($this->userSession->getUser());
160
		$reports = array();
161
162
		foreach ($sharedReports as $sharedReport) {
163
			// shared with a group?
164
			if ((int)$sharedReport['shareType'] === self::SHARE_TYPE_GROUP) {
165
				// is the current user part of this group?
166
				$this->logger->debug('Shareservice: is group share');
167
				if (array_key_exists($sharedReport['shareUid_owner'], $groupsOfUser)) {
168
					// was the report not yet added to the result?
169
					if (!in_array($sharedReport["id"], array_column($reports, "id"))) {
170
						unset($sharedReport['shareType']);
171
						unset($sharedReport['shareUid_owner']);
172
						$sharedReport['isShare'] = self::SHARE_TYPE_GROUP;
173
						$reports[] = $sharedReport;
174
					}
175
				}
176
				// shared with a user directly?
177
			} elseif ((int)$sharedReport['shareType'] === self::SHARE_TYPE_USER) {
178
				// current user matching?
179
				$this->logger->debug('Shareservice: is user share; check against current user: ' . $this->userSession->getUser()
180
																													 ->getUID());
181
				if ($this->userSession->getUser()->getUID() === $sharedReport['shareUid_owner']) {
182
					// was the report not yet added to the result?
183
					$this->logger->debug('Shareservice: Share belongs to current user');
184
					if (!in_array($sharedReport["id"], array_column($reports, "id"))) {
185
						$this->logger->debug('Shareservice: Share added to output');
186
						unset($sharedReport['shareType']);
187
						unset($sharedReport['shareUid_owner']);
188
						$sharedReport['isShare'] = self::SHARE_TYPE_USER;
189
						$reports[] = $sharedReport;
190
					}
191
				}
192
			}
193
		}
194
195
		// no groupings of shares exist for panoramas
196
		if ($item_type !== self::SHARE_ITEM_TYPE_PANORAMA) {
197
			foreach ($reports as $report) {
198
				// if it is a shared group, get all reports below
199
				if ((int)$report['type'] === ReportService::REPORT_TYPE_GROUP) {
200
					$subreport = $this->ReportMapper->getReportsByGroup($report['id']);
201
					$subreport = array_map(function ($report) {
202
						$report['isShare'] = self::SHARE_TYPE_GROUP;
203
						return $report;
204
					}, $subreport);
205
206
					$reports = array_merge($reports, $subreport);
207
				}
208
			}
209
		}
210
		return $reports;
211
	}
212
213
	/**
214
	 * get metadata of a report, shared with current user
215
	 * used to check if user is allowed to execute current report
216
	 *
217
	 * @param $reportId
218
	 * @return array
219
	 * @throws Exception
220
	 */
221
	public function getSharedReport($reportId) {
222
		$sharedReport = $this->getSharedItems(self::SHARE_ITEM_TYPE_REPORT);
223
		if (in_array($reportId, array_column($sharedReport, "id"))) {
224
			$key = array_search($reportId, array_column($sharedReport, 'id'));
225
			return $sharedReport[$key];
226
		} else {
227
			return [];
228
		}
229
	}
230
231
	/**
232
	 * get metadata of a report, shared with current user as part of a panorama
233
	 * used to check if user is allowed to execute current report
234
	 *
235
	 * @param $reportId
236
	 * @return array
237
	 * @throws Exception
238
	 */
239
	public function getSharedPanoramaReport($reportId) {
240
		$foundReportId = 0;
241
		$panoramaOwner = null;
242
		$sharedPanoramas = $this->getSharedItems(self::SHARE_ITEM_TYPE_PANORAMA);
243
		foreach ($sharedPanoramas as $sharedPanorama) {
244
			$panoramaOwner = $sharedPanorama['user_id'];
245
			$pages = json_decode($sharedPanorama['pages'], true);
246
			foreach ($pages as $page) {
247
				$reports = $page['reports'];
248
				foreach ($reports as $report) {
249
					// only use report type 0 = report; not text or image
250
					if ($report['type'] === 0 && $report['value'] === $reportId) {
251
						$foundReportId = $reportId;
252
						break 3;
253
					}
254
				}
255
			}
256
		}
257
258
		if ($foundReportId !== 0) {
0 ignored issues
show
introduced by
The condition $foundReportId !== 0 is always false.
Loading history...
259
			$report = $this->ReportMapper->read($foundReportId);
260
			// check against the original owner of the panorama
261
			// This is needed to prevent getting other reports by modifying the panorama properties
262
			if ($report['user_id'] === $panoramaOwner) {
263
				return $report;
264
			} else {
265
				return [];
266
			}
267
		} else {
268
			return [];
269
		}
270
	}
271
272
	/**
273
	 * Delete an own share (sharee or receiver)
274
	 *
275
	 * @param $shareId
276
	 * @return bool
277
	 * @throws Exception
278
	 */
279
	public function delete($shareId) {
280
		$this->ShareMapper->deleteShare($shareId);
281
		return true;
282
	}
283
284
	/**
285
	 * delete all shares for a report or panorama
286
	 *
287
	 * @param $item_type
288
	 * @param $item_source
289
	 * @return bool
290
	 * @throws Exception
291
	 */
292
	public function deleteSharesByItem($item_type, $item_source) {
293
		return $this->ShareMapper->deleteSharesByItem($item_type, $item_source);
294
	}
295
296
	/**
297
	 * delete all shares when a share-receiving-user is deleted
298
	 *
299
	 * @param $userId
300
	 * @return bool
301
	 */
302
	public function deleteByUser($userId) {
303
		return $this->ShareMapper->deleteByUser($userId);
304
	}
305
306
	/**
307
	 * update/set share password
308
	 *
309
	 * @param $shareId
310
	 * @param string|null $password
311
	 * @param string|null $canEdit
312
	 * @param string|null $domain
313
	 * @return bool
314
	 */
315
	public function update($shareId, $password = null, $canEdit = null, $domain = null) {
316
		if ($password !== null) {
317
			$password = password_hash($password, PASSWORD_DEFAULT);
318
			return $this->ShareMapper->updateSharePassword($shareId, $password);
319
		}
320
		if ($domain !== null) {
321
			return $this->ShareMapper->updateShareDomain($shareId, $domain);
322
		}
323
		if ($canEdit !== null) {
324
			$canEdit === true ? $canEdit = \OCP\Constants::PERMISSION_UPDATE : $canEdit = \OCP\Constants::PERMISSION_READ;
0 ignored issues
show
Bug introduced by
The type OCP\Constants was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
introduced by
The condition $canEdit === true is always false.
Loading history...
325
			return $this->ShareMapper->updateSharePermissions($shareId, $canEdit);
326
		}
327
	}
328
329
	/**
330
	 * generate to token used to authenticate federated shares
331
	 *
332
	 * @return string
333
	 */
334
	private function generateToken() {
335
		$token = $this->secureRandom->generate(15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS);
336
		return $token;
337
	}
338
}