Completed
Push — master ( 41fbda...b85310 )
by Roeland
26:02 queued 13:54
created

GroupPrincipalBackend::groupToPrincipal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 * @copyright Copyright (c) 2018, Georg Ehrke
5
 *
6
 * @author Roeland Jago Douma <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 * @author Georg Ehrke <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
namespace OCA\DAV\DAV;
26
27
use OCP\IGroup;
28
use OCP\IGroupManager;
29
use OCP\IUserSession;
30
use OCP\Share\IManager as IShareManager;
31
use OCP\IL10N;
32
use OCP\IUser;
33
use Sabre\DAV\Exception;
34
use \Sabre\DAV\PropPatch;
35
use Sabre\DAVACL\PrincipalBackend\BackendInterface;
36
37
class GroupPrincipalBackend implements BackendInterface {
38
39
	const PRINCIPAL_PREFIX = 'principals/groups';
40
41
	/** @var IGroupManager */
42
	private $groupManager;
43
44
	/** @var IUserSession */
45
	private $userSession;
46
47
	/** @var IShareManager */
48
	private $shareManager;
49
50
	/** @var IL10N */
51
	private $l10n;
52
53
	/**
54
	 * @param IGroupManager $IGroupManager
55
	 * @param IUserSession $userSession
56
	 * @param IShareManager $shareManager
57
	 * @param IL10N $l10n
58
	 */
59
	public function __construct(IGroupManager $IGroupManager,
60
								IUserSession $userSession,
61
								IShareManager $shareManager,
62
								IL10N $l10n) {
63
		$this->groupManager = $IGroupManager;
64
		$this->userSession = $userSession;
65
		$this->shareManager = $shareManager;
66
		$this->l10n = $l10n;
67
	}
68
69
	/**
70
	 * Returns a list of principals based on a prefix.
71
	 *
72
	 * This prefix will often contain something like 'principals'. You are only
73
	 * expected to return principals that are in this base path.
74
	 *
75
	 * You are expected to return at least a 'uri' for every user, you can
76
	 * return any additional properties if you wish so. Common properties are:
77
	 *   {DAV:}displayname
78
	 *
79
	 * @param string $prefixPath
80
	 * @return string[]
81
	 */
82
	public function getPrincipalsByPrefix($prefixPath) {
83
		$principals = [];
84
85
		if ($prefixPath === self::PRINCIPAL_PREFIX) {
86
			foreach($this->groupManager->search('') as $user) {
87
				$principals[] = $this->groupToPrincipal($user);
88
			}
89
		}
90
91
		return $principals;
92
	}
93
94
	/**
95
	 * Returns a specific principal, specified by it's path.
96
	 * The returned structure should be the exact same as from
97
	 * getPrincipalsByPrefix.
98
	 *
99
	 * @param string $path
100
	 * @return array
101
	 */
102
	public function getPrincipalByPath($path) {
103
		$elements = explode('/', $path,  3);
104
		if ($elements[0] !== 'principals') {
105
			return null;
106
		}
107
		if ($elements[1] !== 'groups') {
108
			return null;
109
		}
110
		$name = urldecode($elements[2]);
111
		$group = $this->groupManager->get($name);
112
113
		if (!is_null($group)) {
114
			return $this->groupToPrincipal($group);
115
		}
116
117
		return null;
118
	}
119
120
	/**
121
	 * Returns the list of members for a group-principal
122
	 *
123
	 * @param string $principal
124
	 * @return string[]
125
	 * @throws Exception
126
	 */
127
	public function getGroupMemberSet($principal) {
128
		$elements = explode('/', $principal);
129
		if ($elements[0] !== 'principals') {
130
			return [];
131
		}
132
		if ($elements[1] !== 'groups') {
133
			return [];
134
		}
135
		$name = $elements[2];
136
		$group = $this->groupManager->get($name);
137
138
		if (is_null($group)) {
139
			return [];
140
		}
141
142
		return array_map(function($user) {
143
			return $this->userToPrincipal($user);
144
		}, $group->getUsers());
145
	}
146
147
	/**
148
	 * Returns the list of groups a principal is a member of
149
	 *
150
	 * @param string $principal
151
	 * @return array
152
	 * @throws Exception
153
	 */
154
	public function getGroupMembership($principal) {
155
		return [];
156
	}
157
158
	/**
159
	 * Updates the list of group members for a group principal.
160
	 *
161
	 * The principals should be passed as a list of uri's.
162
	 *
163
	 * @param string $principal
164
	 * @param string[] $members
165
	 * @throws Exception
166
	 */
167
	public function setGroupMemberSet($principal, array $members) {
168
		throw new Exception('Setting members of the group is not supported yet');
169
	}
170
171
	/**
172
	 * @param string $path
173
	 * @param PropPatch $propPatch
174
	 * @return int
175
	 */
176
	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...
177
		return 0;
178
	}
179
180
	/**
181
	 * @param string $prefixPath
182
	 * @param array $searchProperties
183
	 * @param string $test
184
	 * @return array
185
	 */
186
	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...
187
		$results = [];
188
189
		if (\count($searchProperties) === 0) {
190
			return [];
191
		}
192
		if ($prefixPath !== self::PRINCIPAL_PREFIX) {
193
			return [];
194
		}
195
		// If sharing is disabled, return the empty array
196
		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
197
		if (!$shareAPIEnabled) {
198
			return [];
199
		}
200
201
		// If sharing is restricted to group members only,
202
		// return only members that have groups in common
203
		$restrictGroups = false;
204 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
205
			$user = $this->userSession->getUser();
206
			if (!$user) {
207
				return [];
208
			}
209
210
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
211
		}
212
213
		foreach ($searchProperties as $prop => $value) {
214
			switch ($prop) {
215
				case '{DAV:}displayname':
216
					$groups = $this->groupManager->search($value);
217
218
					$results[] = array_reduce($groups, function(array $carry, IGroup $group) use ($restrictGroups) {
219
						$gid = $group->getGID();
220
						// is sharing restricted to groups only?
221
						if ($restrictGroups !== false) {
222
							if (!\in_array($gid, $restrictGroups, true)) {
223
								return $carry;
224
							}
225
						}
226
227
						$carry[] = self::PRINCIPAL_PREFIX . '/' . $gid;
228
						return $carry;
229
					}, []);
230
					break;
231
232
				default:
233
					$results[] = [];
234
					break;
235
			}
236
		}
237
238
		// results is an array of arrays, so this is not the first search result
239
		// but the results of the first searchProperty
240
		if (count($results) === 1) {
241
			return $results[0];
242
		}
243
244 View Code Duplication
		switch ($test) {
245
			case 'anyof':
246
				return array_values(array_unique(array_merge(...$results)));
247
248
			case 'allof':
249
			default:
250
				return array_values(array_intersect(...$results));
251
		}
252
	}
253
254
	/**
255
	 * @param string $uri
256
	 * @param string $principalPrefix
257
	 * @return string
258
	 */
259
	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...
260
		// If sharing is disabled, return the empty array
261
		$shareAPIEnabled = $this->shareManager->shareApiEnabled();
262
		if (!$shareAPIEnabled) {
263
			return null;
264
		}
265
266
		// If sharing is restricted to group members only,
267
		// return only members that have groups in common
268
		$restrictGroups = false;
269 View Code Duplication
		if ($this->shareManager->shareWithGroupMembersOnly()) {
270
			$user = $this->userSession->getUser();
271
			if (!$user) {
272
				return null;
273
			}
274
275
			$restrictGroups = $this->groupManager->getUserGroupIds($user);
276
		}
277
278
		if (strpos($uri, 'principal:principals/groups/') === 0) {
279
			$name = urlencode(substr($uri, 28));
280
			if ($restrictGroups !== false && !\in_array($name, $restrictGroups, true)) {
281
				return null;
282
			}
283
284
			return substr($uri, 10);
285
		}
286
287
		return null;
288
	}
289
290
	/**
291
	 * @param IGroup $group
292
	 * @return array
293
	 */
294
	protected function groupToPrincipal($group) {
295
		$groupId = $group->getGID();
296
297
		return [
298
			'uri' => 'principals/groups/' . urlencode($groupId),
299
			'{DAV:}displayname' => $this->l10n->t('%s (group)', [$groupId]),
300
			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'GROUP',
301
		];
302
	}
303
304
	/**
305
	 * @param IUser $user
306
	 * @return array
307
	 */
308 View Code Duplication
	protected function userToPrincipal($user) {
309
		$userId = $user->getUID();
310
		$displayName = $user->getDisplayName();
311
312
		$principal = [
313
			'uri' => 'principals/users/' . $userId,
314
			'{DAV:}displayname' => is_null($displayName) ? $userId : $displayName,
315
			'{urn:ietf:params:xml:ns:caldav}calendar-user-type' => 'INDIVIDUAL',
316
		];
317
318
		$email = $user->getEMailAddress();
319
		if (!empty($email)) {
320
			$principal['{http://sabredav.org/ns}email-address'] = $email;
321
		}
322
323
		return $principal;
324
	}
325
}
326