Completed
Push — master ( b25307...4e53ea )
by Morris
46:04 queued 20:51
created

Principal::findByUri()   B

Complexity

Conditions 11
Paths 22

Size

Total Lines 47

Duplication

Lines 14
Ratio 29.79 %

Importance

Changes 0
Metric Value
cc 11
nc 22
nop 2
dl 14
loc 47
rs 7.3166
c 0
b 0
f 0

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