Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/UserCapabilities.php (1 issue)

1
<?php
2
3
namespace Elgg;
4
5
use Elgg\Database\EntityTable;
6
use Elgg\Database\EntityTable\UserFetchFailureException;
7
use ElggAnnotation;
8
use ElggEntity;
9
use ElggFile;
10
use ElggRiverItem;
11
use ElggMetadata;
12
use ElggSession;
13
use InvalidArgumentException;
14
15
/**
16
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
17
 *
18
 * Use the elgg_* versions instead.
19
 *
20
 * @access private
21
 * @since 2.2
22
 */
23
class UserCapabilities {
24
25
	/**
26
	 * @var PluginHooksService $hooks
27
	 */
28
	private $hooks;
29
30
	/**
31
	 * @var EntityTable
32
	 */
33
	private $entities;
34
35
	/**
36
	 * @var ElggSession
37
	 */
38
	private $session;
39
40
	/**
41
	 * Constructor
42
	 *
43
	 * @param PluginHooksService $hooks    Plugin hooks service
44
	 * @param EntityTable        $entities Entity table
45
	 * @param ElggSession        $session  Session
46
	 */
47 498
	public function __construct(PluginHooksService $hooks, EntityTable $entities, ElggSession $session) {
48 498
		$this->hooks = $hooks;
49 498
		$this->entities = $entities;
50 498
		$this->session = $session;
51 498
	}
52
53
	/**
54
	 * Decides if the access system should be ignored for a user.
55
	 *
56
	 * Returns true (meaning ignore access) if either of these 2 conditions are true:
57
	 *   1) an admin user guid is passed to this function.
58
	 *   2) {@link elgg_get_ignore_access()} returns true.
59
	 *
60
	 * @see elgg_set_ignore_access()
61
	 *
62
	 * @param int $user_guid The user to check against.
63
	 *
64
	 * @return bool
65
	 */
66 1251
	public function canBypassPermissionsCheck($user_guid = 0) {
67 1251
		if ($this->session->getIgnoreAccess()) {
68
			// Checking ignored access first to avoid infinite loops,
69
			// when trying to fetch a user by guid
70 632
			return true;
71
		}
72
73
		try {
74 1226
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
75 6
		} catch (UserFetchFailureException $e) {
76 6
			return false;
77
		}
78
79 1220
		return $user && $user->isAdmin();
80
	}
81
82
	/**
83
	 * Can a user edit this entity?
84
	 *
85
	 * @tip Can be overridden by registering for the permissions_check plugin hook.
86
	 *
87
	 * @param ElggEntity $entity    Object entity
88
	 * @param int        $user_guid The user GUID, optionally (default: logged in user)
89
	 *
90
	 * @return bool Whether this entity is editable by the given user.
91
	 * @see elgg_set_ignore_access()
92
	 */
93 399
	public function canEdit(ElggEntity $entity, $user_guid = 0) {
94 399
		if ($this->canBypassPermissionsCheck($user_guid)) {
95 189
			return true;
96
		}
97
98
		try {
99 318
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
100
		} catch (UserFetchFailureException $e) {
101
			return false;
102
		}
103
104
		// Test user if possible - should default to false unless a plugin hook says otherwise
105 318
		$default = call_user_func(function () use ($entity, $user) {
106 318
			if (!$user) {
107 275
				return false;
108
			}
109
110
			// favor the persisted attributes if not saved
111 44
			$attrs = array_merge(
112
					[
113 44
				'owner_guid' => $entity->owner_guid,
114 44
				'container_guid' => $entity->container_guid,
115 44
					], $entity->getOriginalAttributes()
116
			);
117
118 44
			if ($attrs['owner_guid'] == $user->guid) {
119 38
				return true;
120
			}
121
122 34
			if ($attrs['container_guid'] == $user->guid) {
123 3
				return true;
124
			}
125
126 34
			if ($entity->guid == $user->guid) {
127 24
				return true;
128
			}
129
130 14
			$container = $this->entities->get($attrs['container_guid']);
131
132 14
			return ($container && $container->canEdit($user->guid));
133 318
		});
134
135 318
		$params = ['entity' => $entity, 'user' => $user];
136 318
		return $this->hooks->trigger('permissions_check', $entity->getType(), $params, $default);
137
	}
138
139
	/**
140
	 * Can a user delete this entity?
141
	 *
142
	 * @tip Can be overridden by registering for the permissions_check:delete plugin hook.
143
	 *
144
	 * @param ElggEntity $entity    Object entity
145
	 * @param int        $user_guid The user GUID, optionally (default: logged in user)
146
	 *
147
	 * @return bool Whether this entity is deletable by the given user.
148
	 * @since 1.11
149
	 * @see elgg_set_ignore_access()
150
	 */
151 462
	public function canDelete(ElggEntity $entity, $user_guid = 0) {
152 462
		if ($this->canBypassPermissionsCheck($user_guid)) {
153 200
			return true;
154
		}
155
156
		try {
157 272
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
158
		} catch (UserFetchFailureException $e) {
159
			return false;
160
		}
161
162 272
		$return = $entity->canEdit($user_guid);
163
164
		$params = [
165 272
			'entity' => $entity,
166 272
			'user' => $user
167
		];
168 272
		return $this->hooks->trigger('permissions_check:delete', $entity->getType(), $params, $return);
169
	}
170
171
	/**
172
	 * Can a user delete this river item?
173
	 *
174
	 * @tip Can be overridden by registering for the "permissions_check:delete", "river" plugin hook.
175
	 *
176
	 * @param ElggRiverItem $item      River item
177
	 * @param int           $user_guid The user GUID, optionally (default: logged in user)
178
	 *
179
	 * @return bool Whether this river item should be considered deletable by the given user.
180
	 * @since 2.3
181
	 * @see elgg_set_ignore_access()
182
	 */
183 11
	public function canDeleteRiverItem(ElggRiverItem $item, $user_guid = 0) {
184 11
		if ($this->canBypassPermissionsCheck($user_guid)) {
185 5
			return true;
186
		}
187
188
		try {
189 6
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
190
		} catch (UserFetchFailureException $e) {
191
			return false;
192
		}
193
194
		$params = [
195 6
			'item' => $item,
196 6
			'user' => $user,
197
		];
198 6
		return $this->hooks->trigger('permissions_check:delete', 'river', $params, false);
199
	}
200
201
	/**
202
	 * Can a user edit metadata on this entity?
203
	 *
204
	 * If no specific metadata is passed, it returns whether the user can
205
	 * edit any metadata on the entity.
206
	 *
207
	 * @tip Can be overridden by by registering for the permissions_check:metadata
208
	 * plugin hook.
209
	 *
210
	 * @param ElggEntity   $entity    Object entity
211
	 * @param int          $user_guid The user GUID, optionally (default: logged in user)
212
	 * @param ElggMetadata $metadata  The piece of metadata to specifically check or null for any metadata
213
	 *
214
	 * @return bool
215
	 * @see elgg_set_ignore_access()
216
	 */
217 338
	public function canEditMetadata(ElggEntity $entity, $user_guid = 0, ElggMetadata $metadata = null) {
218 338
		if (!$entity->guid) {
219
			// @todo cannot edit metadata on unsaved entity?
220 1
			return false;
221
		}
222
223 337
		if ($this->canBypassPermissionsCheck($user_guid)) {
224 293
			return true;
225
		}
226
227
		try {
228 63
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
229
		} catch (UserFetchFailureException $e) {
230
			return false;
231
		}
232
233 63
		if ($user) {
234 10
			$user_guid = $user->guid;
235
		}
236
237
		// if metadata is not owned or owned by the user, then can edit
238 63
		if ($metadata && ($metadata->owner_guid == 0 || $metadata->owner_guid == $user_guid)) {
239 62
			$return = true;
240
		} else {
241 2
			$return = $entity->canEdit($user_guid);
242
		}
243
244
		// metadata and user may be null
245
		$params = [
246 63
			'entity' => $entity,
247 63
			'user' => $user,
248 63
			'metadata' => $metadata
249
		];
250 63
		return $this->hooks->trigger('permissions_check:metadata', $entity->getType(), $params, $return);
251
	}
252
253
	/**
254
	 * Determines whether or not the user can edit this annotation
255
	 *
256
	 * @param Elggentity     $entity     Object entity
257
	 * @param int            $user_guid  The GUID of the user (defaults to currently logged in user)
258
	 * @param ElggAnnotation $annotation Annotation
259
	 *
260
	 * @return bool
261
	 * @see elgg_set_ignore_access()
262
	 */
263 35
	public function canEditAnnotation(ElggEntity $entity, $user_guid = 0, ElggAnnotation $annotation = null) {
264 35
		if (!$annotation) {
265
			return false;
266
		}
267
268 35
		if ($this->canBypassPermissionsCheck($user_guid)) {
269 33
			return true;
270
		}
271
272
		try {
273 5
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
274
		} catch (UserFetchFailureException $e) {
275
			return false;
276
		}
277
278 5
		$result = false;
279
280 5
		if ($user) {
281
			// If the owner of annotation is the specified user, they can edit.
282 5
			if ($annotation->owner_guid == $user->guid) {
283 4
				$result = true;
284
			}
285
286
			// If the user can edit the entity this is attached to, they can edit.
287 5
			if ($result == false && $entity->canEdit($user->guid)) {
288 1
				$result = true;
289
			}
290
		}
291
292
		// Trigger plugin hook - note that $user may be null
293
		$params = [
294 5
			'entity' => $entity,
295 5
			'user' => $user,
296 5
			'annotation' => $annotation
297
		];
298
299 5
		return $this->hooks->trigger('permissions_check', 'annotation', $params, $result);
300
	}
301
	
302
	/**
303
	 * Can a user add an entity to this container
304
	 *
305
	 * @param ElggEntity $entity    Container entity
306
	 * @param int        $user_guid The GUID of the user creating the entity (0 for logged in user).
307
	 * @param string     $type      The type of entity we're looking to write
308
	 * @param string     $subtype   The subtype of the entity we're looking to write
309
	 *
310
	 * @return bool
311
	 * @see elgg_set_ignore_access()
312
	 */
313 407
	public function canWriteToContainer(ElggEntity $entity, $user_guid = 0, $type = 'all', $subtype = 'all') {
314
		try {
315 407
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
316
		} catch (UserFetchFailureException $e) {
317
			return false;
318
		}
319
320 407
		if ($user) {
321 221
			$user_guid = $user->guid;
322
		}
323
324
		$params = [
325 407
			'container' => $entity,
326 407
			'user' => $user,
327 407
			'subtype' => $subtype
328
		];
329
330
		// Unlike permissions, logic check can be used to prevent certain entity
331
		// types from being contained by other entity types,
332
		// e.g. discussion reply objects can only be contained by discussion objects.
333
		// This hook can also be used to apply status logic, e.g. to disallow
334
		// new replies in closed discussions.
335
		// We do not take a stand hence the return is null. This can be used by
336
		// handlers to check if another hook has modified the value.
337 407
		$logic_check = $this->hooks->trigger('container_logic_check', $type, $params);
0 ignored issues
show
Are you sure the assignment to $logic_check is correct as $this->hooks->trigger('c...check', $type, $params) targeting Elgg\PluginHooksService::trigger() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
338
339 407
		if ($logic_check === false) {
340 2
			return false;
341
		}
342
343 407
		if ($this->canBypassPermissionsCheck($user_guid)) {
344 399
			return true;
345
		}
346
347 15
		$return = false;
348 15
		if ($entity) {
349
			// If the user can edit the container, they can also write to it
350 15
			if ($entity->canEdit($user_guid)) {
351 12
				$return = true;
352
			}
353
		}
354
355
		// Container permissions can prevent users from writing to an entity.
356
		// For instance, these permissions can prevent non-group members from writing
357
		// content to the group.
358 15
		return $this->hooks->trigger('container_permissions_check', $type, $params, $return);
359
	}
360
361
	/**
362
	 * Can a user comment on an entity?
363
	 *
364
	 * @tip Can be overridden by registering for the permissions_check:comment,
365
	 * <entity type> plugin hook.
366
	 *
367
	 * @param ElggEntity $entity    Object entity
368
	 * @param int        $user_guid User guid (default is logged in user)
369
	 * @param bool       $default   Default permission
370
	 * @return bool
371
	 */
372 4
	public function canComment(ElggEntity $entity, $user_guid = 0, $default = null) {
373 4
		if ($this->canBypassPermissionsCheck($user_guid)) {
374 2
			return true;
375
		}
376
377
		try {
378 4
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
379
		} catch (UserFetchFailureException $e) {
380
			return false;
381
		}
382
383
		// By default, we don't take a position of whether commenting is allowed
384
		// because it is handled by the subclasses of \ElggEntity
385
		$params = [
386 4
			'entity' => $entity,
387 4
			'user' => $user
388
		];
389 4
		return $this->hooks->trigger('permissions_check:comment', $entity->getType(), $params, $default);
390
	}
391
392
	/**
393
	 * Can a user annotate an entity?
394
	 *
395
	 * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>,
396
	 * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order.
397
	 *
398
	 * @tip If you want logged out users to annotate an object, do not call
399
	 * canAnnotate(). It's easier than using the plugin hook.
400
	 *
401
	 * @param ElggEntity $entity          Objet entity
402
	 * @param int        $user_guid       User guid (default is logged in user)
403
	 * @param string     $annotation_name The name of the annotation (default is unspecified)
404
	 *
405
	 * @return bool
406
	 */
407 8
	public function canAnnotate(ElggEntity $entity, $user_guid = 0, $annotation_name = '') {
408 8
		if ($annotation_name === null || $annotation_name === false) {
409
			// accepting these for BC
410 1
			$annotation_name = '';
411 8
		} elseif (!is_string($annotation_name)) {
412 4
			throw new InvalidArgumentException(__METHOD__ . ' expects \$annotation_name to be a string');
413
		}
414
415 4
		if ($this->canBypassPermissionsCheck($user_guid)) {
416 2
			return true;
417
		}
418
419
		try {
420 4
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
421
		} catch (UserFetchFailureException $e) {
422
			return false;
423
		}
424
425 4
		$return = (bool) $user;
426
427
		$params = [
428 4
			'entity' => $entity,
429 4
			'user' => $user,
430 4
			'annotation_name' => $annotation_name,
431
		];
432
433 4
		if (!empty($annotation_name)) {
434 4
			$return = $this->hooks->trigger("permissions_check:annotate:$annotation_name", $entity->getType(), $params, $return);
435
		}
436
437 4
		return $this->hooks->trigger('permissions_check:annotate', $entity->getType(), $params, $return);
438
	}
439
440
	/**
441
	 * Can a user download a file?
442
	 *
443
	 * @tip Can be overridden by registering for the permissions_check:download,file plugin hook.
444
	 *
445
	 * @param ElggFile $entity    File entity
446
	 * @param int      $user_guid User guid (default is logged in user)
447
	 * @param bool     $default   Default permission
448
	 *
449
	 * @return bool
450
	 */
451 2
	public function canDownload(ElggFile $entity, $user_guid = 0, $default = true) {
452 2
		if ($this->canBypassPermissionsCheck($user_guid)) {
453 1
			return true;
454
		}
455
456
		try {
457 2
			$user = $this->entities->getUserForPermissionsCheck($user_guid);
458
		} catch (UserFetchFailureException $e) {
459
			return false;
460
		}
461
462
		$params = [
463 2
			'entity' => $entity,
464 2
			'user' => $user
465
		];
466
467 2
		return $this->hooks->trigger('permissions_check:download', 'file', $params, $default);
468
	}
469
470
}
471