Completed
Push — master ( 80b27f...d0ec0c )
by Morris
50:16 queued 34:06
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
use Sabre\HTTP\URLUtil;
43
44
class Principal implements BackendInterface {
45
46
	/** @var IUserManager */
47
	private $userManager;
48
49
	/** @var IGroupManager */
50
	private $groupManager;
51
52
	/** @var IShareManager */
53
	private $shareManager;
54
55
	/** @var IUserSession */
56
	private $userSession;
57
58
	/** @var string */
59
	private $principalPrefix;
60
61
	/** @var bool */
62
	private $hasGroups;
63
64
	/**
65
	 * @param IUserManager $userManager
66
	 * @param IGroupManager $groupManager
67
	 * @param IShareManager $shareManager
68
	 * @param IUserSession $userSession
69
	 * @param string $principalPrefix
70
	 */
71
	public function __construct(IUserManager $userManager,
72
								IGroupManager $groupManager,
73
								IShareManager $shareManager,
74
								IUserSession $userSession,
75
								$principalPrefix = 'principals/users/') {
76
		$this->userManager = $userManager;
77
		$this->groupManager = $groupManager;
78
		$this->shareManager = $shareManager;
79
		$this->userSession = $userSession;
80
		$this->principalPrefix = trim($principalPrefix, '/');
81
		$this->hasGroups = ($principalPrefix === 'principals/users/');
82
	}
83
84
	/**
85
	 * Returns a list of principals based on a prefix.
86
	 *
87
	 * This prefix will often contain something like 'principals'. You are only
88
	 * expected to return principals that are in this base path.
89
	 *
90
	 * You are expected to return at least a 'uri' for every user, you can
91
	 * return any additional properties if you wish so. Common properties are:
92
	 *   {DAV:}displayname
93
	 *
94
	 * @param string $prefixPath
95
	 * @return string[]
96
	 */
97
	public function getPrincipalsByPrefix($prefixPath) {
98
		$principals = [];
99
100
		if ($prefixPath === $this->principalPrefix) {
101
			foreach($this->userManager->search('') as $user) {
102
				$principals[] = $this->userToPrincipal($user);
103
			}
104
		}
105
106
		return $principals;
107
	}
108
109
	/**
110
	 * Returns a specific principal, specified by it's path.
111
	 * The returned structure should be the exact same as from
112
	 * getPrincipalsByPrefix.
113
	 *
114
	 * @param string $path
115
	 * @return array
116
	 */
117
	public function getPrincipalByPath($path) {
118
		list($prefix, $name) = \Sabre\Uri\split($path);
119
120
		if ($prefix === $this->principalPrefix) {
121
			$user = $this->userManager->get($name);
122
123
			if ($user !== null) {
124
				return $this->userToPrincipal($user);
125
			}
126
		}
127
		return null;
128
	}
129
130
	/**
131
	 * Returns the list of members for a group-principal
132
	 *
133
	 * @param string $principal
134
	 * @return string[]
135
	 * @throws Exception
136
	 */
137 View Code Duplication
	public function getGroupMemberSet($principal) {
138
		// TODO: for now the group principal has only one member, the user itself
139
		$principal = $this->getPrincipalByPath($principal);
140
		if (!$principal) {
141
			throw new Exception('Principal not found');
142
		}
143
144
		return [$principal['uri']];
145
	}
146
147
	/**
148
	 * Returns the list of groups a principal is a member of
149
	 *
150
	 * @param string $principal
151
	 * @param bool $needGroups
152
	 * @return array
153
	 * @throws Exception
154
	 */
155
	public function getGroupMembership($principal, $needGroups = false) {
156
		list($prefix, $name) = \Sabre\Uri\split($principal);
157
158
		if ($prefix === $this->principalPrefix) {
159
			$user = $this->userManager->get($name);
160
			if (!$user) {
161
				throw new Exception('Principal not found');
162
			}
163
164
			if ($this->hasGroups || $needGroups) {
165
				$groups = $this->groupManager->getUserGroups($user);
166
				$groups = array_map(function($group) {
167
					/** @var IGroup $group */
168
					return 'principals/groups/' . urlencode($group->getGID());
169
				}, $groups);
170
171
				return $groups;
172
			}
173
		}
174
		return [];
175
	}
176
177
	/**
178
	 * Updates the list of group members for a group principal.
179
	 *
180
	 * The principals should be passed as a list of uri's.
181
	 *
182
	 * @param string $principal
183
	 * @param string[] $members
184
	 * @throws Exception
185
	 */
186
	public function setGroupMemberSet($principal, array $members) {
187
		throw new Exception('Setting members of the group is not supported yet');
188
	}
189
190
	/**
191
	 * @param string $path
192
	 * @param PropPatch $propPatch
193
	 * @return int
194
	 */
195
	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...
196
		return 0;
197
	}
198
199
	/**
200
	 * Search user principals
201
	 *
202
	 * @param array $searchProperties
203
	 * @param string $test
204
	 * @return array
205
	 */
206
	protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
207
		$results = [];
208
209
		// If sharing is disabled, return the empty array
210
		if (!$this->shareManager->shareApiEnabled()) {
211
			return [];
212
		}
213
214
		// If sharing is restricted to group members only,
215
		// return only members that have groups in common
216
		$restrictGroups = false;
217 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
218
			$user = $this->userSession->getUser();
219
			if (!$user) {
220
				return [];
221
			}
222
223
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
224
		}
225
226
		foreach ($searchProperties as $prop => $value) {
227
			switch ($prop) {
228
				case '{http://sabredav.org/ns}email-address':
229
					$users = $this->userManager->getByEmail($value);
230
231
					$results[] = array_reduce($users, function(array $carry, IUser $user) use ($restrictGroups) {
232
						// is sharing restricted to groups only?
233 View Code Duplication
						if ($restrictGroups !== false) {
234
							$userGroups = $this->groupManager->getUserGroupIds($user);
235
							if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
236
								return $carry;
237
							}
238
						}
239
240
						$carry[] = $this->principalPrefix . '/' . $user->getUID();
241
						return $carry;
242
					}, []);
243
					break;
244
245
				default:
246
					$results[] = [];
247
					break;
248
			}
249
		}
250
251
		// results is an array of arrays, so this is not the first search result
252
		// but the results of the first searchProperty
253
		if (count($results) === 1) {
254
			return $results[0];
255
		}
256
257
		switch ($test) {
258
			case 'anyof':
259
				return array_unique(array_merge(...$results));
260
261
			case 'allof':
262
			default:
263
				return array_intersect(...$results);
264
		}
265
	}
266
267
	/**
268
	 * @param string $prefixPath
269
	 * @param array $searchProperties
270
	 * @param string $test
271
	 * @return array
272
	 */
273
	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...
274
		if (count($searchProperties) === 0) {
275
			return [];
276
		}
277
278
		switch ($prefixPath) {
279
			case 'principals/users':
280
				return $this->searchUserPrincipals($searchProperties, $test);
281
282
			default:
283
				return [];
284
		}
285
	}
286
287
	/**
288
	 * @param string $uri
289
	 * @param string $principalPrefix
290
	 * @return string
291
	 */
292
	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...
293
		// If sharing is disabled, return null as in user not found
294
		if (!$this->shareManager->shareApiEnabled()) {
295
			return null;
296
		}
297
298
		// If sharing is restricted to group members only,
299
		// return only members that have groups in common
300
		$restrictGroups = false;
301 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
302
			$user = $this->userSession->getUser();
303
			if (!$user) {
304
				return null;
305
			}
306
307
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
308
		}
309
310
		if (strpos($uri, 'mailto:') === 0) {
311
			if ($principalPrefix === 'principals/users') {
312
				$users = $this->userManager->getByEmail(substr($uri, 7));
313
				if (count($users) !== 1) {
314
					return null;
315
				}
316
				$user = $users[0];
317
318 View Code Duplication
				if ($restrictGroups !== false) {
319
					$userGroups = $this->groupManager->getUserGroupIds($user);
320
					if (count(array_intersect($userGroups, $restrictGroups)) === 0) {
321
						return null;
322
					}
323
				}
324
325
				return $this->principalPrefix . '/' . $user->getUID();
326
			}
327
		}
328
329
		return null;
330
	}
331
332
	/**
333
	 * @param IUser $user
334
	 * @return array
335
	 */
336
	protected function userToPrincipal($user) {
337
		$userId = $user->getUID();
338
		$displayName = $user->getDisplayName();
339
		$principal = [
340
				'uri' => $this->principalPrefix . '/' . $userId,
341
				'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
342
		];
343
344
		$email = $user->getEMailAddress();
345
		if (!empty($email)) {
346
			$principal['{http://sabredav.org/ns}email-address'] = $email;
347
		}
348
349
		return $principal;
350
	}
351
352
	public function getPrincipalPrefix() {
353
		return $this->principalPrefix;
354
	}
355
356
}
357