Passed
Pull Request — master (#1281)
by René
04:06
created

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