Passed
Pull Request — master (#1320)
by René
04:05
created

Acl::requestYesVotes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
cc 2
nc 2
nop 0
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
use OCA\Polls\Exceptions\VoteLimitExceededException;
31
32
use OCP\IUserManager;
33
use OCP\IGroupManager;
34
use OCA\Polls\Db\Poll;
35
use OCA\Polls\Db\Share;
36
use OCA\Polls\Db\PollMapper;
37
use OCA\Polls\Db\VoteMapper;
38
use OCA\Polls\Db\ShareMapper;
39
40
/**
41
 * Class Acl
42
 *
43
 * @package OCA\Polls\Model\Acl
44
 */
45
class Acl implements JsonSerializable {
46
47
	/** @var IUserManager */
48
	private $userManager;
49
50
	/** @var IGroupManager */
51
	private $groupManager;
52
53
	/** @var PollMapper */
54
	private $pollMapper;
55
56
	/** @var VoteMapper */
57
	private $voteMapper;
58
59
	/** @var ShareMapper */
60
	private $shareMapper;
61
62
	/** @var Poll */
63
	private $poll;
64
65
	/** @var Share */
66
	private $share;
67
68
	public function __construct(
69
		IUserManager $userManager,
70
		IGroupManager $groupManager,
71
		PollMapper $pollMapper,
72
		VoteMapper $voteMapper,
73
		ShareMapper $shareMapper
74
	) {
75
		$this->userManager = $userManager;
76
		$this->groupManager = $groupManager;
77
		$this->pollMapper = $pollMapper;
78
		$this->voteMapper = $voteMapper;
79
		$this->shareMapper = $shareMapper;
80
		$this->poll = new Poll;
81
		$this->share = new Share;
82
	}
83
84
85
	/**
86
	 * load share via token and than call setShare
87
	 */
88
	public function setToken(string $token = ''): Acl {
89
		try {
90
			return $this->setShare($this->shareMapper->findByToken($token));
91
		} catch (DoesNotExistException $e) {
92
			throw new NotAuthorizedException('Error loading share ' . $token);
93
		}
94
	}
95
96
	/**
97
	 * setShare - sets and validates the share
98
	 * read access is
99
	 */
100
	public function setShare(Share $share): Acl {
101
		$this->share = $share;
102
		$this->validateShareAccess();
103
		$this->setPollId($share->getPollId());
104
		$this->requestView();
105
		return $this;
106
	}
107
108
	public function setPollId(int $pollId = 0): Acl {
109
		try {
110
			return $this->setPoll($this->pollMapper->find($pollId));
111
		} catch (DoesNotExistException $e) {
112
			throw new NotAuthorizedException('Error loading poll ' . $pollId);
113
		}
114
	}
115
116
	public function setPoll(Poll $poll): Acl {
117
		$this->poll = $poll;
118
		return $this;
119
	}
120
121
	public function getUserId() {
122
		if ($this->getLoggedIn()) {
123
			return \OC::$server->getUserSession()->getUser()->getUID();
124
		} else {
125
			return $this->share->getUserId();
126
		}
127
	}
128
129
	private function getDisplayName(): string {
130
		if ($this->getLoggedIn()) {
131
			return $this->userManager->get($this->getUserId())->getDisplayName();
132
		} else {
133
			return $this->share->getDisplayName();
134
		}
135
	}
136
137
	public function getPollId(): int {
138
		return $this->poll->getId();
139
	}
140
141
	public function getAllowYesVote(): bool {
142
		return !($this->poll->getVoteLimit() && $this->getYesVotes() >= $this->poll->getVoteLimit());
143
	}
144
145
	public function getOptionLimit(): int {
146
		return $this->poll->getOptionLimit();
147
	}
148
149
	private function getYesVotes(): int {
150
		return $this->voteMapper->countYesVotes($this->getUserId(), $this->getPollId());
151
	}
152
153
	public function getAllowView(): bool {
154
		return (
155
			   $this->getAllowEdit()
156
			|| !$this->poll->getDeleted() && (
157
				   $this->getValidPublicShare()
158
				|| $this->getUserIsInvolved()
159
				|| $this->getPublicShare()
160
			)
161
		);
162
	}
163
164
	public function getAllowVote(): bool {
165
		return ($this->getAllowView() || $this->getToken())
166
			&& !$this->poll->getExpired()
167
			&& !$this->poll->getDeleted()
168
			&& $this->getUserId();
169
	}
170
171
	public function getAllowSubscribe(): bool {
172
		return ($this->hasEmail())
173
			&& !$this->poll->getDeleted()
174
			&& $this->getAllowView();
175
	}
176
177
	public function getAllowComment(): bool {
178
		return !$this->poll->getDeleted() && $this->getUserId();
179
	}
180
181
	public function getAllowEdit(): bool {
182
		return ($this->getIsOwner() || $this->hasAdminAccess());
183
	}
184
185
	public function requestView(): void {
186
		if (!$this->getAllowView()) {
187
			throw new NotAuthorizedException;
188
		}
189
	}
190
191
	public function requestVote(): void {
192
		if (!$this->getAllowVote()) {
193
			throw new NotAuthorizedException;
194
		}
195
	}
196
197
	public function requestComment(): void {
198
		if (!$this->getAllowComment()) {
199
			throw new NotAuthorizedException;
200
		}
201
	}
202
203
	public function requestEdit(): void {
204
		if (!$this->getAllowEdit()) {
205
			throw new NotAuthorizedException;
206
		}
207
	}
208
209
	public function requestYesVotes(): void {
210
		if (!$this->getAllowYesVote()) {
211
			throw new VoteLimitExceededException;
212
		}
213
	}
214
215
	public function requestDelete(): void {
216
		if (!$this->getAllowEdit() && !$this->getIsAdmin()) {
217
			throw new NotAuthorizedException;
218
		}
219
	}
220
221
	public function requestTakeOver(): void {
222
		if (!$this->getIsAdmin()) {
223
			throw new NotAuthorizedException;
224
		}
225
	}
226
227
	public function validateUserId(string $userId): void {
228
		if ($this->getUserId() !== $userId) {
229
			throw new NotAuthorizedException;
230
		}
231
	}
232
233
	public function getAllowSeeResults(): bool {
234
		return $this->poll->getShowResults() === Poll::SHOW_RESULTS_ALWAYS
235
			|| ($this->poll->getShowResults() === 'expired' && $this->poll->getExpired())
236
			|| $this->getIsOwner();
237
	}
238
239
	public function getAllowSeeUsernames(): bool {
240
		return !$this->poll->getAnonymous() || $this->getIsOwner();
241
	}
242
243
	public function getToken(): string {
244
		return strval($this->share->getToken());
245
	}
246
247
	public function jsonSerialize(): array {
248
		return	[
249
			'allowComment'      => $this->getAllowComment(),
250
			'allowEdit'         => $this->getAllowEdit(),
251
			'allowSeeResults'   => $this->getAllowSeeResults(),
252
			'allowSeeUsernames' => $this->getAllowSeeUsernames(),
253
			'allowSubscribe'    => $this->getAllowSubscribe(),
254
			'allowView'         => $this->getAllowView(),
255
			'allowVote'         => $this->getAllowVote(),
256
			'displayName'       => $this->getDisplayName(),
257
			'isOwner'           => $this->getIsOwner(),
258
			'loggedIn'			=> $this->getLoggedIn(),
259
			'pollId'            => $this->getPollId(),
260
			'token'             => $this->getToken(),
261
			'userHasVoted'		=> $this->getUserHasVoted(),
262
			'userId'            => $this->getUserId(),
263
			'userIsInvolved'	=> $this->getUserIsInvolved(),
264
		];
265
	}
266
267
	/**
268
	 * getLoggedIn - Is user logged in to nextcloud?
269
	 */
270
	private function getLoggedIn(): bool {
271
		return \OC::$server->getUserSession()->isLoggedIn();
272
	}
273
274
	/**
275
	 * getIsOwner - Is user owner of the poll?
276
	 */
277
	private function getIsOwner(): bool {
278
		return ($this->getLoggedIn() && $this->poll->getOwner() === $this->getUserId());
279
	}
280
281
	/**
282
	 * getIsAdmin - Is the user admin
283
	 * Returns true, if user is in admin group
284
	 */
285
	private function getIsAdmin(): bool {
286
		return ($this->getLoggedIn() && $this->groupManager->isAdmin($this->getUserId()));
287
	}
288
289
	/**
290
	 * hasAdminAccess - Has user administrative rights?
291
	 * Returns true, if user is in admin group and poll has allowed admins to manage the poll
292
	 */
293
	private function hasAdminAccess(): bool {
294
		return ($this->getIsAdmin() && $this->poll->getAdminAccess());
295
	}
296
297
	/**
298
	 * getUserIsInvolved - Is user involved?
299
	 * Returns true, if the current user is involved in the share via share or if he is a participant.
300
	 */
301
	private function getUserIsInvolved(): bool {
302
		return (
303
			   $this->getIsOwner()
304
			|| $this->getUserHasVoted()
305
			|| $this->getGroupShare()
306
			|| $this->getPersonalShare());
307
	}
308
309
	/**
310
	 * getUserHasVoted - Is user a participant?
311
	 * Returns true, if the current user is already a particitipant of the current poll.
312
	 */
313
	private function getUserHasVoted(): bool {
314
		return count(
315
			$this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId())
316
		) > 0;
317
	}
318
319
	/**
320
	 * getGroupShare - Is the poll shared via group share?
321
	 * Returns true, if the current poll contains a group share with a group,
322
	 * where the current user is member of. This only affects logged users.
323
	 */
324
	private function getGroupShare(): int {
325
		if (!$this->getLoggedIn()) {
326
			return 0;
327
		}
328
		return count(
329
			array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
330
				if ($item->getType() === Share::TYPE_GROUP && $this->groupManager->isInGroup($this->getUserId(), $item->getUserId())) {
331
					return true;
332
				}
333
			})
334
		);
335
	}
336
337
	/**
338
	 * getPersonalShare - Is the poll shared via user share?
339
	 * Returns >0, if the current poll contains a user share for the current user.
340
	 * This only affects logged users.
341
	 */
342
	private function getPersonalShare(): int {
343
		if (!$this->getLoggedIn()) {
344
			return 0;
345
		}
346
		return count(
347
			array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
348
				if (in_array($item->getType(), [
349
					Share::TYPE_USER,
350
					Share::TYPE_EXTERNAL,
351
					Share::TYPE_EMAIL,
352
					Share::TYPE_CONTACT
353
				])
354
					&& $item->getUserId() === $this->getUserId()
355
				) {
356
					return true;
357
				}
358
			})
359
		);
360
	}
361
362
	/**
363
	 * getPublicShare
364
	 */
365
	private function getPublicShare(): int {
366
		return count(
367
			array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
368
				if ($item->getType() === Share::TYPE_PUBLIC && $item->getToken() === $this->getToken()) {
369
					return true;
370
				}
371
			})
372
		);
373
	}
374
375
	/**
376
	 * validateShareAccess
377
	 */
378
	private function validateShareAccess(): void {
379
		if ($this->getLoggedIn()) {
380
			if (!$this->getValidAuthenticatedShare()) {
381
				throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for external users');
382
			};
383
		} else {
384
			if (!$this->getValidPublicShare()) {
385
				throw new NotAuthorizedException('Share type "' . $this->share->getType() . '"only valid for internal users');
386
			};
387
		}
388
	}
389
390
	/**
391
	 * getValidPublicShare
392
	 */
393
	private function getValidPublicShare(): bool {
394
		return in_array($this->share->getType(), [
395
			Share::TYPE_PUBLIC,
396
			Share::TYPE_EMAIL,
397
			Share::TYPE_CONTACT,
398
			Share::TYPE_EXTERNAL
399
		]);
400
	}
401
402
	/**
403
	 * getValidAuthenticatedShare
404
	 */
405
	private function getValidAuthenticatedShare(): bool {
406
		return in_array($this->share->getType(), [
407
			Share::TYPE_PUBLIC,
408
			Share::TYPE_USER,
409
			Share::TYPE_GROUP
410
		]);
411
	}
412
413
	/**
414
	 * hasEmail
415
	 */
416
	private function hasEmail(): bool {
417
		if ($this->share->getToken()) {
418
			return strlen($this->share->getEmailAddress()) > 0;
419
		} else {
420
			return $this->getLoggedIn();
421
		}
422
	}
423
}
424