Completed
Pull Request — master (#9268)
by Roeland
21:18
created

Principal::searchUserPrincipals()   C

Complexity

Conditions 11
Paths 26

Size

Total Lines 60
Code Lines 33

Duplication

Lines 14
Ratio 23.33 %

Importance

Changes 0
Metric Value
cc 11
eloc 33
nc 26
nop 2
dl 14
loc 60
rs 6.2926
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) 2016, ownCloud, Inc.
4
 *
5
 * @author Bart Visscher <[email protected]>
6
 * @author Jakob Sack <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Lukas Reschke <[email protected]>
9
 * @author Morris Jobke <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Thomas Tanghus <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OCA\DAV\Connector\Sabre;
32
33
use OCP\IGroup;
34
use OCP\IGroupManager;
35
use OCP\IUser;
36
use OCP\IUserManager;
37
use OCP\IUserSession;
38
use OCP\Share\IManager as IShareManager;
39
use Sabre\DAV\Exception;
40
use \Sabre\DAV\PropPatch;
41
use Sabre\DAVACL\PrincipalBackend\BackendInterface;
42
43
class Principal implements BackendInterface {
44
45
	/** @var IUserManager */
46
	private $userManager;
47
48
	/** @var IGroupManager */
49
	private $groupManager;
50
51
	/** @var IShareManager */
52
	private $shareManager;
53
54
	/** @var IUserSession */
55
	private $userSession;
56
57
	/** @var string */
58
	private $principalPrefix;
59
60
	/** @var bool */
61
	private $hasGroups;
62
63
	/**
64
	 * @param IUserManager $userManager
65
	 * @param IGroupManager $groupManager
66
	 * @param IShareManager $shareManager
67
	 * @param IUserSession $userSession
68
	 * @param string $principalPrefix
69
	 */
70
	public function __construct(IUserManager $userManager,
71
								IGroupManager $groupManager,
72
								IShareManager $shareManager,
73
								IUserSession $userSession,
74
								$principalPrefix = 'principals/users/') {
75
		$this->userManager = $userManager;
76
		$this->groupManager = $groupManager;
77
		$this->shareManager = $shareManager;
78
		$this->userSession = $userSession;
79
		$this->principalPrefix = trim($principalPrefix, '/');
80
		$this->hasGroups = ($principalPrefix === 'principals/users/');
81
	}
82
83
	/**
84
	 * Returns a list of principals based on a prefix.
85
	 *
86
	 * This prefix will often contain something like 'principals'. You are only
87
	 * expected to return principals that are in this base path.
88
	 *
89
	 * You are expected to return at least a 'uri' for every user, you can
90
	 * return any additional properties if you wish so. Common properties are:
91
	 *   {DAV:}displayname
92
	 *
93
	 * @param string $prefixPath
94
	 * @return string[]
95
	 */
96
	public function getPrincipalsByPrefix($prefixPath) {
97
		$principals = [];
98
99
		if ($prefixPath === $this->principalPrefix) {
100
			foreach($this->userManager->search('') as $user) {
101
				$principals[] = $this->userToPrincipal($user);
102
			}
103
		}
104
105
		return $principals;
106
	}
107
108
	/**
109
	 * Returns a specific principal, specified by it's path.
110
	 * The returned structure should be the exact same as from
111
	 * getPrincipalsByPrefix.
112
	 *
113
	 * @param string $path
114
	 * @return array
115
	 */
116
	public function getPrincipalByPath($path) {
117
		list($prefix, $name) = \Sabre\Uri\split($path);
118
119
		if ($prefix === $this->principalPrefix) {
120
			$user = $this->userManager->get($name);
121
122
			if ($user !== null) {
123
				return $this->userToPrincipal($user);
124
			}
125
		}
126
		return null;
127
	}
128
129
	/**
130
	 * Returns the list of members for a group-principal
131
	 *
132
	 * @param string $principal
133
	 * @return string[]
134
	 * @throws Exception
135
	 */
136 View Code Duplication
	public function getGroupMemberSet($principal) {
137
		// TODO: for now the group principal has only one member, the user itself
138
		$principal = $this->getPrincipalByPath($principal);
139
		if (!$principal) {
140
			throw new Exception('Principal not found');
141
		}
142
143
		return [$principal['uri']];
144
	}
145
146
	/**
147
	 * Returns the list of groups a principal is a member of
148
	 *
149
	 * @param string $principal
150
	 * @param bool $needGroups
151
	 * @return array
152
	 * @throws Exception
153
	 */
154
	public function getGroupMembership($principal, $needGroups = false) {
155
		list($prefix, $name) = \Sabre\Uri\split($principal);
156
157
		if ($prefix === $this->principalPrefix) {
158
			$user = $this->userManager->get($name);
159
			if (!$user) {
160
				throw new Exception('Principal not found');
161
			}
162
163
			if ($this->hasGroups || $needGroups) {
164
				$groups = $this->groupManager->getUserGroups($user);
165
				$groups = array_map(function($group) {
166
					/** @var IGroup $group */
167
					return 'principals/groups/' . urlencode($group->getGID());
168
				}, $groups);
169
170
				return $groups;
171
			}
172
		}
173
		return [];
174
	}
175
176
	/**
177
	 * Updates the list of group members for a group principal.
178
	 *
179
	 * The principals should be passed as a list of uri's.
180
	 *
181
	 * @param string $principal
182
	 * @param string[] $members
183
	 * @throws Exception
184
	 */
185
	public function setGroupMemberSet($principal, array $members) {
186
		throw new Exception('Setting members of the group is not supported yet');
187
	}
188
189
	/**
190
	 * @param string $path
191
	 * @param PropPatch $propPatch
192
	 * @return int
193
	 */
194
	function updatePrincipal($path, PropPatch $propPatch) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
195
		return 0;
196
	}
197
198
	/**
199
	 * Search user principals
200
	 *
201
	 * @param array $searchProperties
202
	 * @param string $test
203
	 * @return array
204
	 */
205
	protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
206
		$results = [];
207
208
		// If sharing is disabled, return the empty array
209
		if (!$this->shareManager->shareApiEnabled()) {
210
			return [];
211
		}
212
213
		// If sharing is restricted to group members only,
214
		// return only members that have groups in common
215
		$restrictGroups = false;
216 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
217
			$user = $this->userSession->getUser();
218
			if (!$user) {
219
				return [];
220
			}
221
222
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
223
		}
224
225
		foreach ($searchProperties as $prop => $value) {
226
			switch ($prop) {
227
				case '{http://sabredav.org/ns}email-address':
228
					$users = $this->userManager->getByEmail($value);
229
230
					$results[] = array_reduce($users, function(array $carry, IUser $user) use ($restrictGroups) {
231
						// is sharing restricted to groups only?
232 View Code Duplication
						if ($restrictGroups !== false) {
233
							$userGroups = $this->groupManager->getUserGroupIds($user);
234
							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
235
								return $carry;
236
							}
237
						}
238
239
						$carry[] = $this->principalPrefix . '/' . $user->getUID();
240
						return $carry;
241
					}, []);
242
					break;
243
244
				default:
245
					$results[] = [];
246
					break;
247
			}
248
		}
249
250
		// results is an array of arrays, so this is not the first search result
251
		// but the results of the first searchProperty
252
		if (count($results) === 1) {
253
			return $results[0];
254
		}
255
256
		switch ($test) {
257
			case 'anyof':
258
				return array_unique(array_merge(...$results));
259
260
			case 'allof':
261
			default:
262
				return array_intersect(...$results);
263
		}
264
	}
265
266
	/**
267
	 * @param string $prefixPath
268
	 * @param array $searchProperties
269
	 * @param string $test
270
	 * @return array
271
	 */
272
	function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
273
		if (count($searchProperties) === 0) {
274
			return [];
275
		}
276
277
		switch ($prefixPath) {
278
			case 'principals/users':
279
				return $this->searchUserPrincipals($searchProperties, $test);
280
281
			default:
282
				return [];
283
		}
284
	}
285
286
	/**
287
	 * @param string $uri
288
	 * @param string $principalPrefix
289
	 * @return string
290
	 */
291
	function findByUri($uri, $principalPrefix) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
292
		// If sharing is disabled, return null as in user not found
293
		if (!$this->shareManager->shareApiEnabled()) {
294
			return null;
295
		}
296
297
		// If sharing is restricted to group members only,
298
		// return only members that have groups in common
299
		$restrictGroups = false;
300 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
301
			$user = $this->userSession->getUser();
302
			if (!$user) {
303
				return null;
304
			}
305
306
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
307
		}
308
309
		if (strpos($uri, 'mailto:') === 0) {
310
			if ($principalPrefix === 'principals/users') {
311
				$users = $this->userManager->getByEmail(substr($uri, 7));
312
				if (count($users) !== 1) {
313
					return null;
314
				}
315
				$user = $users[0];
316
317 View Code Duplication
				if ($restrictGroups !== false) {
318
					$userGroups = $this->groupManager->getUserGroupIds($user);
319
					if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
320
						return null;
321
					}
322
				}
323
324
				return $this->principalPrefix . '/' . $user->getUID();
325
			}
326
		}
327
		if (substr($uri, 0, 10) === 'principal:') {
328
			$principal = substr($uri, 10);
329
			$principal = $this->getPrincipalByPath($principal);
330
			if ($principal !== null) {
331
				return $principal['uri'];
332
			}
333
		}
334
335
		return null;
336
	}
337
338
	/**
339
	 * @param IUser $user
340
	 * @return array
341
	 */
342
	protected function userToPrincipal($user) {
343
		$userId = $user->getUID();
344
		$displayName = $user->getDisplayName();
345
		$principal = [
346
				'uri' => $this->principalPrefix . '/' . $userId,
347
				'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
348
		];
349
350
		$email = $user->getEMailAddress();
351
		if (!empty($email)) {
352
			$principal['{http://sabredav.org/ns}email-address'] = $email;
353
		}
354
355
		return $principal;
356
	}
357
358
	public function getPrincipalPrefix() {
359
		return $this->principalPrefix;
360
	}
361
362
}
363