Passed
Push — master ( 31cc07...56b08c )
by Joas
24:48 queued 11s
created

UserPlugin::search()   F

Complexity

Conditions 41
Paths > 20000

Size

Total Lines 169
Code Lines 112

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 41
eloc 112
nc 54208
nop 4
dl 0
loc 169
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Arthur Schiwon <[email protected]>
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Georg Ehrke <[email protected]>
8
 * @author Joas Schilling <[email protected]>
9
 * @author John Molakvoæ (skjnldsv) <[email protected]>
10
 * @author Julius Härtl <[email protected]>
11
 * @author Morris Jobke <[email protected]>
12
 * @author Robin Appelman <[email protected]>
13
 * @author Roeland Jago Douma <[email protected]>
14
 * @author Thomas Citharel <[email protected]>
15
 *
16
 * @license GNU AGPL version 3 or any later version
17
 *
18
 * This program is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License as
20
 * published by the Free Software Foundation, either version 3 of the
21
 * License, or (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License
29
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30
 *
31
 */
32
33
namespace OC\Collaboration\Collaborators;
34
35
use OC\KnownUser\KnownUserService;
36
use OCP\Collaboration\Collaborators\ISearchPlugin;
37
use OCP\Collaboration\Collaborators\ISearchResult;
38
use OCP\Collaboration\Collaborators\SearchResultType;
39
use OCP\IConfig;
40
use OCP\IGroupManager;
41
use OCP\IUser;
42
use OCP\IUserManager;
43
use OCP\IUserSession;
44
use OCP\Share\IShare;
45
use OCP\UserStatus\IManager as IUserStatusManager;
46
47
class UserPlugin implements ISearchPlugin {
48
	/* @var bool */
49
	protected $shareWithGroupOnly;
50
	/* @var bool */
51
	protected $shareeEnumeration;
52
	/* @var bool */
53
	protected $shareeEnumerationInGroupOnly;
54
	/* @var bool */
55
	protected $shareeEnumerationPhone;
56
	/* @var bool */
57
	protected $shareeEnumerationFullMatch;
58
59
	/** @var IConfig */
60
	private $config;
61
	/** @var IGroupManager */
62
	private $groupManager;
63
	/** @var IUserSession */
64
	private $userSession;
65
	/** @var IUserManager */
66
	private $userManager;
67
	/** @var KnownUserService */
68
	private $knownUserService;
69
	/** @var IUserStatusManager */
70
	private $userStatusManager;
71
72
	public function __construct(IConfig $config,
73
								IUserManager $userManager,
74
								IGroupManager $groupManager,
75
								IUserSession $userSession,
76
								KnownUserService $knownUserService,
77
								IUserStatusManager $userStatusManager) {
78
		$this->config = $config;
79
80
		$this->groupManager = $groupManager;
81
		$this->userSession = $userSession;
82
		$this->userManager = $userManager;
83
		$this->knownUserService = $knownUserService;
84
		$this->userStatusManager = $userStatusManager;
85
86
		$this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
87
		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
88
		$this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
89
		$this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
90
		$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
91
	}
92
93
	public function search($search, $limit, $offset, ISearchResult $searchResult) {
94
		$result = ['wide' => [], 'exact' => []];
95
		$users = [];
96
		$hasMoreResults = false;
97
98
		$currentUserId = $this->userSession->getUser()->getUID();
99
		$currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser());
100
		if ($this->shareWithGroupOnly) {
101
			// Search in all the groups this user is part of
102
			foreach ($currentUserGroups as $userGroupId) {
103
				$usersInGroup = $this->groupManager->displayNamesInGroup($userGroupId, $search, $limit, $offset);
104
				foreach ($usersInGroup as $userId => $displayName) {
105
					$userId = (string) $userId;
106
					$user = $this->userManager->get($userId);
107
					if (!$user->isEnabled()) {
108
						// Ignore disabled users
109
						continue;
110
					}
111
					$users[$userId] = $user;
112
				}
113
				if (count($usersInGroup) >= $limit) {
114
					$hasMoreResults = true;
115
				}
116
			}
117
		} else {
118
			// Search in all users
119
			$usersTmp = $this->userManager->searchDisplayName($search, $limit, $offset);
120
			foreach ($usersTmp as $user) {
121
				if ($user->isEnabled()) { // Don't keep deactivated users
122
					$users[$user->getUID()] = $user;
123
				}
124
			}
125
		}
126
127
		$this->takeOutCurrentUser($users);
128
129
		if (!$this->shareeEnumeration || count($users) < $limit) {
130
			$hasMoreResults = true;
131
		}
132
133
		$foundUserById = false;
134
		$lowerSearch = strtolower($search);
135
		$userStatuses = $this->userStatusManager->getUserStatuses(array_keys($users));
136
		foreach ($users as $uid => $user) {
137
			$userDisplayName = $user->getDisplayName();
138
			$userEmail = $user->getEMailAddress();
139
			$uid = (string) $uid;
140
141
			$status = [];
142
			if (array_key_exists($uid, $userStatuses)) {
143
				$userStatus = $userStatuses[$uid];
144
				$status = [
145
					'status' => $userStatus->getStatus(),
146
					'message' => $userStatus->getMessage(),
147
					'icon' => $userStatus->getIcon(),
148
					'clearAt' => $userStatus->getClearAt()
149
						? (int)$userStatus->getClearAt()->format('U')
150
						: null,
151
				];
152
			}
153
154
155
			if (
156
				$this->shareeEnumerationFullMatch &&
157
				$lowerSearch !== '' && (strtolower($uid) === $lowerSearch ||
158
				strtolower($userDisplayName) === $lowerSearch ||
159
				strtolower($userEmail) === $lowerSearch)
160
			) {
161
				if (strtolower($uid) === $lowerSearch) {
162
					$foundUserById = true;
163
				}
164
				$result['exact'][] = [
165
					'label' => $userDisplayName,
166
					'subline' => $status['message'] ?? '',
167
					'icon' => 'icon-user',
168
					'value' => [
169
						'shareType' => IShare::TYPE_USER,
170
						'shareWith' => $uid,
171
					],
172
					'shareWithDisplayNameUnique' => !empty($userEmail) ? $userEmail : $uid,
173
					'status' => $status,
174
				];
175
			} else {
176
				$addToWideResults = false;
177
				if ($this->shareeEnumeration &&
178
					!($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone)) {
179
					$addToWideResults = true;
180
				}
181
182
				if ($this->shareeEnumerationPhone && $this->knownUserService->isKnownToUser($currentUserId, $user->getUID())) {
183
					$addToWideResults = true;
184
				}
185
186
				if (!$addToWideResults && $this->shareeEnumerationInGroupOnly) {
187
					$commonGroups = array_intersect($currentUserGroups, $this->groupManager->getUserGroupIds($user));
188
					if (!empty($commonGroups)) {
189
						$addToWideResults = true;
190
					}
191
				}
192
193
				if ($addToWideResults) {
194
					$result['wide'][] = [
195
						'label' => $userDisplayName,
196
						'subline' => $status['message'] ?? '',
197
						'icon' => 'icon-user',
198
						'value' => [
199
							'shareType' => IShare::TYPE_USER,
200
							'shareWith' => $uid,
201
						],
202
						'shareWithDisplayNameUnique' => !empty($userEmail) ? $userEmail : $uid,
203
						'status' => $status,
204
					];
205
				}
206
			}
207
		}
208
209
		if ($this->shareeEnumerationFullMatch && $offset === 0 && !$foundUserById) {
210
			// On page one we try if the search result has a direct hit on the
211
			// user id and if so, we add that to the exact match list
212
			$user = $this->userManager->get($search);
213
			if ($user instanceof IUser) {
214
				$addUser = true;
215
216
				if ($this->shareWithGroupOnly) {
217
					// Only add, if we have a common group
218
					$commonGroups = array_intersect($currentUserGroups, $this->groupManager->getUserGroupIds($user));
219
					$addUser = !empty($commonGroups);
220
				}
221
222
				if ($addUser) {
223
					$status = [];
224
					$uid = $user->getUID();
225
					$userEmail = $user->getEMailAddress();
226
					if (array_key_exists($user->getUID(), $userStatuses)) {
227
						$userStatus = $userStatuses[$user->getUID()];
228
						$status = [
229
							'status' => $userStatus->getStatus(),
230
							'message' => $userStatus->getMessage(),
231
							'icon' => $userStatus->getIcon(),
232
							'clearAt' => $userStatus->getClearAt()
233
								? (int)$userStatus->getClearAt()->format('U')
234
								: null,
235
						];
236
					}
237
238
					$result['exact'][] = [
239
						'label' => $user->getDisplayName(),
240
						'icon' => 'icon-user',
241
						'subline' => $status['message'] ?? '',
242
						'value' => [
243
							'shareType' => IShare::TYPE_USER,
244
							'shareWith' => $user->getUID(),
245
						],
246
						'shareWithDisplayNameUnique' => $userEmail !== null && $userEmail !== '' ? $userEmail : $uid,
247
						'status' => $status,
248
					];
249
				}
250
			}
251
		}
252
253
254
255
		$type = new SearchResultType('users');
256
		$searchResult->addResultSet($type, $result['wide'], $result['exact']);
257
		if (count($result['exact'])) {
258
			$searchResult->markExactIdMatch($type);
259
		}
260
261
		return $hasMoreResults;
262
	}
263
264
	public function takeOutCurrentUser(array &$users) {
265
		$currentUser = $this->userSession->getUser();
266
		if (!is_null($currentUser)) {
267
			if (isset($users[$currentUser->getUID()])) {
268
				unset($users[$currentUser->getUID()]);
269
			}
270
		}
271
	}
272
}
273