Passed
Pull Request — master (#1254)
by René
05:23 queued 01:48
created

Acl::validateShareAccess()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 7
cp 0
rs 10
cc 4
nc 4
nop 0
crap 20
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
	 * set
94
	 * @return self
95
	 * @throws NotAuthorizedException
96
	 */
97
	public function set($pollId = 0, $token = ''): Acl {
98
		if ($token) {
99
			$pollId = $this->setToken($token);
100
		}
101
102
		try {
103
			$this->poll = $this->pollMapper->find($pollId);
104
		} catch (DoesNotExistException $e) {
105
			throw new NotAuthorizedException;
106
		}
107
108
		return $this;
109
	}
110
111
	/**
112
	 * getUserId
113
	 * @return string
114
	 */
115
	public function getUserId() {
116
		if ($this->getLoggedIn()) {
117
			return \OC::$server->getUserSession()->getUser()->getUID();
118
		} else {
119
			return $this->share->getUserId();
120
		}
121
	}
122
123
	/**
124
	 * getDisplayName
125
	 * @return string
126
	 */
127
	private function getDisplayName() {
128
		if ($this->getLoggedIn()) {
129
			return $this->userManager->get($this->getUserId())->getDisplayName();
130
		} else {
131
			return $this->share->getDisplayName();
132
		}
133
	}
134
135
	/**
136
	 * getPollId
137
	 * @return int
138
	 */
139
	public function getPollId(): int {
140
		return $this->poll->getId();
141
	}
142
143
	/**
144
	 * getAllowView
145
	 * @return bool
146
	 */
147
	public function getAllowView(): bool {
148
		return (
149
			   $this->getAllowEdit()
150
			|| !$this->poll->getDeleted() && (
151
				   $this->getUserIsInvolved()
152
				|| $this->getPublicShare()
153
				|| ($this->poll->getAccess() === Poll::ACCESS_PUBLIC)
154
			)
155
		);
156
	}
157
158
	/**
159
	 * getAllowVote
160
	 * @return bool
161
	 */
162
	public function getAllowVote(): bool {
163
		return ($this->getAllowView() || $this->getToken())
164
			&& !$this->poll->getExpired()
165
			&& !$this->poll->getDeleted()
166
			&& $this->getUserId();
167
	}
168
169
	/**
170
	 * getAllowSubscribe
171
	 * @return bool
172
	 */
173
	public function getAllowSubscribe(): bool {
174
		return ($this->hasEmail())
175
			&& !$this->poll->getDeleted()
176
			&& $this->getAllowView();
177
	}
178
179
	/**
180
	 * getAllowComment
181
	 * @return bool
182
	 */
183
	public function getAllowComment(): bool {
184
		return !$this->poll->getDeleted() && $this->getUserId();
185
	}
186
187
	/**
188
	 * getAllowEdit
189
	 * @return bool
190
	 */
191
	public function getAllowEdit(): bool {
192
		return ($this->getIsOwner() || $this->getIsAdmin());
193
	}
194
195
	/**
196
	 * getAllowSeeResults
197
	 * @return bool
198
	 */
199
	public function getAllowSeeResults(): bool {
200
		return $this->poll->getShowResults() === Poll::SHOW_RESULTS_ALWAYS
201
			|| ($this->poll->getShowResults() === 'expired' && $this->poll->getExpired())
202
			|| $this->getIsOwner();
203
	}
204
205
	/**
206
	 * getAllowSeeUsernames
207
	 * @return bool
208
	 */
209
	public function getAllowSeeUsernames(): bool {
210
		return !$this->poll->getAnonymous() || $this->getIsOwner();
211
	}
212
213
	/**
214
	 * getToken
215
	 * @return string
216
	 */
217
	public function getToken(): string {
218
		return strval($this->share->getToken());
219
	}
220
221
	/**
222
	 * @return array
223
	 */
224
	public function jsonSerialize(): array {
225
		return	[
226
			'allowComment'      => $this->getAllowComment(),
227
			'allowEdit'         => $this->getAllowEdit(),
228
			'allowSeeResults'   => $this->getAllowSeeResults(),
229
			'allowSeeUsernames' => $this->getAllowSeeUsernames(),
230
			'allowSubscribe'    => $this->getAllowSubscribe(),
231
			'allowView'         => $this->getAllowView(),
232
			'allowVote'         => $this->getAllowVote(),
233
			'displayName'       => $this->getDisplayName(),
234
			'isOwner'           => $this->getIsOwner(),
235
			'loggedIn'			=> $this->getLoggedIn(),
236
			'pollId'            => $this->getPollId(),
237
			'token'             => $this->getToken(),
238
			'userHasVoted'		=> $this->getUserHasVoted(),
239
			'userId'            => $this->getUserId(),
240
			'userIsInvolved'	=> $this->getUserIsInvolved(),
241
		];
242
	}
243
244
	/**
245
	 * setToken
246
	 * @param string $token
247
	 * @return int
248
	 * @throws NotAuthorizedException
249
	 */
250
	private function setToken($token) {
251
		try {
252
			$this->share = $this->shareMapper->findByToken($token);
253
			$this->validateShareAccess();
254
		} catch (DoesNotExistException $e) {
255
			if (!$this->getLoggedIn()) {
256
				// Token is invalid and user is not logged in. Reject
257
				throw new NotAuthorizedException;
258
			}
259
		}
260
		return $this->share->getPollId();
261
	}
262
263
	/**
264
	 * getLoggedIn - Is user logged in to nextcloud?
265
	 * @return string
266
	 */
267
	private function getLoggedIn() {
268
		return \OC::$server->getUserSession()->isLoggedIn();
269
	}
270
271
	/**
272
	 * getIsOwner - Is user owner of the poll?
273
	 * @return bool
274
	 */
275
	private function getIsOwner(): bool {
276
		return ($this->getLoggedIn() && $this->poll->getOwner() === $this->getUserId());
277
	}
278
279
	/**
280
	 * getIsAdmin - Has user administrative rights?
281
	 * Returns true, if user is in admin group and poll has allowed admins to manage the poll
282
	 * @return bool
283
	 */
284
	private function getIsAdmin(): bool {
285
		return ($this->getLoggedIn() && $this->groupManager->isAdmin($this->getUserId()) && $this->poll->getAdminAccess());
286
	}
287
288
	/**
289
	 * getIsShared - is the poll shared?
290
	 * Returns true, if any share exists for the current poll.
291
	 * @return bool
292
	 */
293
	private function getIsShared(): bool {
0 ignored issues
show
Unused Code introduced by
The method getIsShared() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
294
		return ($this->getGroupShare() || $this->getPersonalShare() || $this->getPublicShare());
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
	 * @return bool
301
	 */
302
	private function getUserIsInvolved(): bool {
303
		return (
304
			   $this->getIsOwner()
305
			|| $this->getUserHasVoted()
306
			|| $this->getGroupShare()
307
			|| $this->getPersonalShare());
308
	}
309
310
	/**
311
	 * getUserHasVoted - Is user a participant?
312
	 * Returns true, if the current user is already a particitipant of the current poll.
313
	 * @return bool
314
	 */
315
	private function getUserHasVoted(): bool {
316
		return count(
317
			$this->voteMapper->findParticipantsVotes($this->getPollId(), $this->getUserId())
318
		) > 0;
319
	}
320
321
	/**
322
	 * getGroupShare - Is the poll shared via group share?
323
	 * Returns true, if the current poll contains a group share with a group,
324
	 * where the current user is member of. This only affects logged users.
325
	 * @return bool
326
	 */
327
	private function getGroupShare(): bool {
328
		if ($this->getLoggedIn()) {
329
			return count(
330
				array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
331
					if ($item->getType() === Share::TYPE_GROUP && $this->groupManager->isInGroup($this->getUserId(), $item->getUserId())) {
332
						return true;
333
					}
334
				})
335
			);
336
		} else {
337
			return false;
338
		}
339
	}
340
341
	/**
342
	 * getPersonalShare - Is the poll shared via user share?
343
	 * Returns true, if the current poll contains a user share for the current user.
344
	 * This only affects logged users.
345
	 * @return bool
346
	 */
347
	private function getPersonalShare(): bool {
348
		if ($this->getLoggedIn()) {
349
			return count(
350
				array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
351
					if (
352
						($item->getType() === Share::TYPE_USER
353
							|| $item->getType() === Share::TYPE_EXTERNAL
354
							|| $item->getType() === Share::TYPE_EMAIL
355
							|| $item->getType() === Share::TYPE_CONTACT
356
						)
357
						&& $item->getUserId() === $this->getUserId()
358
					) {
359
						return true;
360
					}
361
				})
362
			);
363
		} else {
364
			return false;
365
		}
366
	}
367
368
	/**
369
	 * getPublicShare
370
	 * @return bool
371
	 */
372
	private function getPublicShare(): bool {
373
		return count(
374
			array_filter($this->shareMapper->findByPoll($this->getPollId()), function ($item) {
375
				if ($item->getType() === Share::TYPE_PUBLIC && $item->getToken() === $this->getToken()) {
376
					return true;
377
				}
378
			})
379
		);
380
	}
381
382
	/**
383
	 * validateShareAccess
384
	 * @return bool
385
	 * @throws NotAuthorizedException
386
	 */
387
	private function validateShareAccess() {
388
		if (\OC::$server->getUserSession()->getUser()->getUID()) {
389
			if (!getValidAuthenticatedShare()) {
0 ignored issues
show
Bug introduced by
The function getValidAuthenticatedShare was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

389
			if (!/** @scrutinizer ignore-call */ getValidAuthenticatedShare()) {
Loading history...
390
				throw new NotAuthorizedException;
391
			};
392
		} else {
393
			if (!getValidPublicShare()) {
0 ignored issues
show
Bug introduced by
The function getValidPublicShare was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

393
			if (!/** @scrutinizer ignore-call */ getValidPublicShare()) {
Loading history...
394
				throw new NotAuthorizedException;
395
			};
396
		}
397
	}
398
399
	/**
400
	 * getValidPublicShare
401
	 * @return bool
402
	 */
403
	private function getValidPublicShare() {
0 ignored issues
show
Unused Code introduced by
The method getValidPublicShare() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
404
		return in_array($this->share->getType(), [
405
			Share::TYPE_PUBLIC,
406
			Share::TYPE_EMAIL,
407
			Share::TYPE_CONTACT,
408
			Share::TYPE_EXTERNAL
409
		]);
410
	}
411
412
	/**
413
	 * getValidAuthenticatedShare
414
	 * @return bool
415
	 */
416
	private function getValidAuthenticatedShare() {
0 ignored issues
show
Unused Code introduced by
The method getValidAuthenticatedShare() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
417
		return in_array($this->share->getType(), [
418
			Share::TYPE_PUBLIC,
419
			Share::TYPE_USER,
420
			Share::TYPE_GROUP
421
		]);
422
	}
423
424
	/**
425
	 * hasEmail
426
	 * @return bool
427
	 */
428
	private function hasEmail():bool {
429
		if ($this->share->getToken()) {
430
			return strlen($this->share->getEmailAddress()) > 0;
431
		} else {
432
			return $this->getLoggedIn();
433
		}
434
	}
435
}
436