Passed
Push — master ( 18ab32...6cb597 )
by René
04:13
created

Acl::setPollId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017 Vinzenz Rosenkranz <[email protected]>
4
 *
5
 * @author René Gieling <[email protected]>
6
 *
7
 * @license GNU AGPL version 3 or any later version
8
 *
9
 *  This program is free software: you can redistribute it and/or modify
10
 *  it under the terms of the GNU Affero General Public License as
11
 *  published by the Free Software Foundation, either version 3 of the
12
 *  License, or (at your option) any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU Affero General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU Affero General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 *
22
 */
23
24
25
namespace OCA\Polls\Model;
26
27
use JsonSerializable;
28
use OCP\AppFramework\Db\DoesNotExistException;
29
use OCA\Polls\Exceptions\NotAuthorizedException;
30
31
use OCP\IUserManager;
32
use OCP\IGroupManager;
33
use OCA\Polls\Db\Poll;
34
use OCA\Polls\Db\Share;
35
use OCA\Polls\Db\PollMapper;
36
use OCA\Polls\Db\VoteMapper;
37
use OCA\Polls\Db\ShareMapper;
38
39
/**
40
 * Class Acl
41
 *
42
 * @package OCA\Polls\Model\Acl
43
 */
44
class Acl implements JsonSerializable {
45
46
	/** @var IUserManager */
47
	private $userManager;
48
49
	/** @var IGroupManager */
50
	private $groupManager;
51
52
	/** @var PollMapper */
53
	private $pollMapper;
54
55
	/** @var VoteMapper */
56
	private $voteMapper;
57
58
	/** @var ShareMapper */
59
	private $shareMapper;
60
61
	/** @var Poll */
62
	private $poll;
63
64
	/** @var Share */
65
	private $share;
66
67
	/**
68
	 * Acl constructor.
69
	 * @param IUserManager $userManager
70
	 * @param IGroupManager $groupManager
71
	 * @param PollMapper $pollMapper
72
	 * @param VoteMapper $voteMapper
73
	 * @param ShareMapper $shareMapper
74
	 *
75
	 */
76
	public function __construct(
77
		IUserManager $userManager,
78
		IGroupManager $groupManager,
79
		PollMapper $pollMapper,
80
		VoteMapper $voteMapper,
81
		ShareMapper $shareMapper
82
	) {
83
		$this->userManager = $userManager;
84
		$this->groupManager = $groupManager;
85
		$this->pollMapper = $pollMapper;
86
		$this->voteMapper = $voteMapper;
87
		$this->shareMapper = $shareMapper;
88
		$this->poll = new Poll;
89
		$this->share = new Share;
90
	}
91
92
93
	/**
94
	 * setToken - load share via token and than call setShare
95
	 * @param string $token
96
	 * @return self
97
	 * @throws NotAuthorizedException
98
	 */
99
	public function setToken($token = ''): Acl {
100
		try {
101
			return $this->setShare($this->shareMapper->findByToken($token));
102
		} catch (DoesNotExistException $e) {
103
			throw new NotAuthorizedException('Error loading share ' . $token);
104
		}
105
	}
106
107
	/**
108
	 * setShare - sets and validates the share
109
	 * read access is
110
	 * @param Share $share
111
	 * @return Acl
112
	 */
113
	public function setShare(Share $share): Acl {
114
		$this->share = $share;
115
		$this->validateShareAccess();
116
117
		// load poll, if pollId does not match
118
		if ($this->share->getPollId() !== $this->poll->getId()) {
119
			$this->setPollId($share->getPollId());
120
		}
121
		return $this;
122
	}
123
124
	/**
125
	 * setPollId
126
	 * @param int $pollId
127
	 * @return Acl
128
	 * @throws NotAuthorizedException
129
	 */
130
	public function setPollId(int $pollId = 0): Acl {
131
		try {
132
			return $this->setPoll($this->pollMapper->find($pollId));
133
		} catch (DoesNotExistException $e) {
134
			throw new NotAuthorizedException('Error loading poll ' . $pollId);
135
		}
136
	}
137
138
	/**
139
	 * setPoll
140
	 * @param Poll $poll
141
	 * @return Acl
142
	 * @throws NotAuthorizedException
143
	 */
144
	public function setPoll(Poll $poll) {
145
		$this->poll = $poll;
146
		$this->requestView();
147
		return $this;
148
	}
149
150
	/**
151
	 * getUserId
152
	 * @return string
153
	 */
154
	public function getUserId() {
155
		if ($this->getLoggedIn()) {
156
			return \OC::$server->getUserSession()->getUser()->getUID();
157
		} else {
158
			return $this->share->getUserId();
159
		}
160
	}
161
162
	/**
163
	 * getDisplayName
164
	 * @return string
165
	 */
166
	private function getDisplayName() {
167
		if ($this->getLoggedIn()) {
168
			return $this->userManager->get($this->getUserId())->getDisplayName();
169
		} else {
170
			return $this->share->getDisplayName();
171
		}
172
	}
173
174
	/**
175
	 * getPollId
176
	 * @return int
177
	 */
178
	public function getPollId(): int {
179
		return $this->poll->getId();
180
	}
181
182
	/**
183
	 * getAllowView
184
	 * @return bool
185
	 */
186
	public function getAllowView(): bool {
187
		return (
188
			   $this->getAllowEdit()
189
			|| !$this->poll->getDeleted() && (
190
				   $this->getValidPublicShare()
191
				|| $this->getUserIsInvolved()
192
				|| $this->getPublicShare()
193
			)
194
		);
195
	}
196
197
	/**
198
	 * getAllowVote
199
	 * @return bool
200
	 */
201
	public function getAllowVote(): bool {
202
		return ($this->getAllowView() || $this->getToken())
203
			&& !$this->poll->getExpired()
204
			&& !$this->poll->getDeleted()
205
			&& $this->getUserId();
206
	}
207
208
	/**
209
	 * getAllowSubscribe
210
	 * @return bool
211
	 */
212
	public function getAllowSubscribe(): bool {
213
		return ($this->hasEmail())
214
			&& !$this->poll->getDeleted()
215
			&& $this->getAllowView();
216
	}
217
218
	/**
219
	 * getAllowComment
220
	 * @return bool
221
	 */
222
	public function getAllowComment(): bool {
223
		return !$this->poll->getDeleted() && $this->getUserId();
224
	}
225
226
	/**
227
	 * getAllowEdit
228
	 * @return bool
229
	 */
230
	public function getAllowEdit(): bool {
231
		return ($this->getIsOwner() || $this->getIsAdmin());
232
	}
233
234
	/**
235
	 * requestView
236
	 * @throws NotAuthorizedException
237
	 * @return void
238
	 */
239
	public function requestView(): void {
240
		if (!$this->getAllowView()) {
241
			throw new NotAuthorizedException;
242
		}
243
	}
244
245
	/**
246
	 * requestVote
247
	 * @throws NotAuthorizedException
248
	 * @return void
249
	 */
250
	public function requestVote(): void {
251
		if (!$this->getAllowVote()) {
252
			throw new NotAuthorizedException;
253
		}
254
	}
255
256
	/**
257
	 * requestComment
258
	 * @throws NotAuthorizedException
259
	 * @return void
260
	 */
261
	public function requestComment(): void {
262
		if (!$this->getAllowComment()) {
263
			throw new NotAuthorizedException;
264
		}
265
	}
266
267
	/**
268
	 * requestEdit
269
	 * @throws NotAuthorizedException
270
	 * @return void
271
	 */
272
	public function requestEdit(): void {
273
		if (!$this->getAllowEdit()) {
274
			throw new NotAuthorizedException;
275
		}
276
	}
277
278
	/**
279
	 * requestDelete
280
	 * @throws NotAuthorizedException
281
	 * @return void
282
	 */
283
	public function requestDelete(): void {
284
		if (!$this->getAllowEdit() || !$this->poll->getDeleted()) {
285
			throw new NotAuthorizedException;
286
		}
287
	}
288
289
	/**
290
	 * validateUserId
291
	 * @throws NotAuthorizedException
292
	 * @return void
293
	 */
294
	public function validateUserId($userId): void {
295
		if ($this->getUserId() !== $userId) {
296
			throw new NotAuthorizedException;
297
		}
298
	}
299
300
	/**
301
	 * getAllowSeeResults
302
	 * @return bool
303
	 */
304
	public function getAllowSeeResults(): bool {
305
		return $this->poll->getShowResults() === Poll::SHOW_RESULTS_ALWAYS
306
			|| ($this->poll->getShowResults() === 'expired' && $this->poll->getExpired())
307
			|| $this->getIsOwner();
308
	}
309
310
	/**
311
	 * getAllowSeeUsernames
312
	 * @return bool
313
	 */
314
	public function getAllowSeeUsernames(): bool {
315
		return !$this->poll->getAnonymous() || $this->getIsOwner();
316
	}
317
318
	/**
319
	 * getToken
320
	 * @return string
321
	 */
322
	public function getToken(): string {
323
		return strval($this->share->getToken());
324
	}
325
326
	/**
327
	 * @return array
328
	 */
329
	public function jsonSerialize(): array {
330
		return	[
331
			'allowComment'      => $this->getAllowComment(),
332
			'allowEdit'         => $this->getAllowEdit(),
333
			'allowSeeResults'   => $this->getAllowSeeResults(),
334
			'allowSeeUsernames' => $this->getAllowSeeUsernames(),
335
			'allowSubscribe'    => $this->getAllowSubscribe(),
336
			'allowView'         => $this->getAllowView(),
337
			'allowVote'         => $this->getAllowVote(),
338
			'displayName'       => $this->getDisplayName(),
339
			'isOwner'           => $this->getIsOwner(),
340
			'loggedIn'			=> $this->getLoggedIn(),
341
			'pollId'            => $this->getPollId(),
342
			'token'             => $this->getToken(),
343
			'userHasVoted'		=> $this->getUserHasVoted(),
344
			'userId'            => $this->getUserId(),
345
			'userIsInvolved'	=> $this->getUserIsInvolved(),
346
		];
347
	}
348
349
	/**
350
	 * getLoggedIn - Is user logged in to nextcloud?
351
	 * @return bool
352
	 */
353
	private function getLoggedIn(): bool {
354
		return \OC::$server->getUserSession()->isLoggedIn();
355
	}
356
357
	/**
358
	 * getIsOwner - Is user owner of the poll?
359
	 * @return bool
360
	 */
361
	private function getIsOwner(): bool {
362
		return ($this->getLoggedIn() && $this->poll->getOwner() === $this->getUserId());
363
	}
364
365
	/**
366
	 * getIsAdmin - Has user administrative rights?
367
	 * Returns true, if user is in admin group and poll has allowed admins to manage the poll
368
	 * @return bool
369
	 */
370
	private function getIsAdmin(): bool {
371
		return ($this->getLoggedIn() && $this->groupManager->isAdmin($this->getUserId()) && $this->poll->getAdminAccess());
372
	}
373
374
	/**
375
	 * getUserIsInvolved - Is user involved?
376
	 * Returns true, if the current user is involved in the share via share or if he is a participant.
377
	 * @return bool
378
	 */
379
	private function getUserIsInvolved(): bool {
380
		return (
381
			   $this->getIsOwner()
382
			|| $this->getUserHasVoted()
383
			|| $this->getGroupShare()
384
			|| $this->getPersonalShare());
385
	}
386
387
	/**
388
	 * getUserHasVoted - Is user a participant?
389
	 * Returns true, if the current user is already a particitipant of the current poll.
390
	 * @return bool
391
	 */
392
	private function getUserHasVoted(): bool {
393
		return count(
394
			$this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId())
395
		) > 0;
396
	}
397
398
	/**
399
	 * getGroupShare - Is the poll shared via group share?
400
	 * Returns true, if the current poll contains a group share with a group,
401
	 * where the current user is member of. This only affects logged users.
402
	 * @return bool
403
	 */
404
	private function getGroupShare(): bool {
405
		if ($this->getLoggedIn()) {
406
			return count(
407
				array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
408
					if ($item->getType() === Share::TYPE_GROUP && $this->groupManager->isInGroup($this->getUserId(), $item->getUserId())) {
409
						return true;
410
					}
411
				})
412
			);
413
		} else {
414
			return false;
415
		}
416
	}
417
418
	/**
419
	 * getPersonalShare - Is the poll shared via user share?
420
	 * Returns true, if the current poll contains a user share for the current user.
421
	 * This only affects logged users.
422
	 * @return bool
423
	 */
424
	private function getPersonalShare(): bool {
425
		if ($this->getLoggedIn()) {
426
			return count(
427
				array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
428
					if (in_array($item->getType(), [
429
						Share::TYPE_USER,
430
						Share::TYPE_EXTERNAL,
431
						Share::TYPE_EMAIL,
432
						Share::TYPE_CONTACT
433
					])
434
						&& $item->getUserId() === $this->getUserId()
435
					) {
436
						return true;
437
					}
438
				})
439
			);
440
		} else {
441
			return false;
442
		}
443
	}
444
445
	/**
446
	 * getPublicShare
447
	 * @return bool
448
	 */
449
	private function getPublicShare(): bool {
450
		return count(
451
			array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
452
				if ($item->getType() === Share::TYPE_PUBLIC && $item->getToken() === $this->getToken()) {
453
					return true;
454
				}
455
			})
456
		);
457
	}
458
459
	/**
460
	 * validateShareAccess
461
	 * @return void
462
	 * @throws NotAuthorizedException
463
	 */
464
	private function validateShareAccess(): void {
465
		if ($this->getLoggedIn()) {
466
			if (!$this->getValidAuthenticatedShare()) {
467
				throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for external users');
468
			};
469
		} else {
470
			if (!$this->getValidPublicShare()) {
471
				throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for internal users');
472
			};
473
		}
474
	}
475
476
	/**
477
	 * getValidPublicShare
478
	 * @return bool
479
	 */
480
	private function getValidPublicShare(): bool {
481
		return in_array($this->share->getType(), [
482
			Share::TYPE_PUBLIC,
483
			Share::TYPE_EMAIL,
484
			Share::TYPE_CONTACT,
485
			Share::TYPE_EXTERNAL
486
		]);
487
	}
488
489
	/**
490
	 * getValidAuthenticatedShare
491
	 * @return bool
492
	 */
493
	private function getValidAuthenticatedShare(): bool {
494
		return in_array($this->share->getType(), [
495
			Share::TYPE_PUBLIC,
496
			Share::TYPE_USER,
497
			Share::TYPE_GROUP
498
		]);
499
	}
500
501
	/**
502
	 * hasEmail
503
	 * @return bool
504
	 */
505
	private function hasEmail(): bool {
506
		if ($this->share->getToken()) {
507
			return strlen($this->share->getEmailAddress()) > 0;
508
		} else {
509
			return $this->getLoggedIn();
510
		}
511
	}
512
}
513