Passed
Pull Request — master (#1516)
by Daniel
03:43
created

ShareService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 10
dl 0
loc 22
ccs 0
cts 9
cp 0
rs 9.9332
c 2
b 0
f 0
cc 1
nc 1
nop 10
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
namespace OCA\Polls\Service;
25
26
use Psr\Log\LoggerInterface;
27
use OCP\AppFramework\Db\DoesNotExistException;
28
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
29
use OCA\Polls\Exceptions\NotAuthorizedException;
30
use OCA\Polls\Exceptions\InvalidShareTypeException;
31
use OCA\Polls\Exceptions\ShareAlreadyExistsException;
32
use OCA\Polls\Exceptions\NotFoundException;
33
34
use OCP\Security\ISecureRandom;
35
use OCP\IGroupManager;
36
use OCA\Polls\Db\ShareMapper;
37
use OCA\Polls\Db\Share;
38
use OCA\Polls\Model\Acl;
39
use OCA\Polls\Model\UserGroupClass;
40
41
class ShareService {
42
43
	/** @var LoggerInterface */
44
	private $logger;
45
46
	/** @var string */
47
	private $appName;
48
49
	/** @var string|null */
50
	private $userId;
51
52
	/** @var IGroupManager */
53
	private $groupManager;
54
55
	/** @var SystemService */
56
	private $systemService;
57
58
	/** @var ShareMapper */
59
	private $shareMapper;
60
61
	/** @var Share */
62
	private $share;
63
64
	/** @var MailService */
65
	private $mailService;
66
67
	/** @var Acl */
68
	private $acl;
69
70
	/** @var NotificationService */
71
	private $notificationService;
72
73
	public function __construct(
74
		string $AppName,
75
		LoggerInterface $logger,
76
		?string $UserId,
77
		IGroupManager $groupManager,
78
		SystemService $systemService,
79
		ShareMapper $shareMapper,
80
		Share $share,
81
		MailService $mailService,
82
		Acl $acl,
83
		NotificationService $notificationService
84
	) {
85
		$this->appName = $AppName;
86
		$this->logger = $logger;
87
		$this->userId = $UserId;
88
		$this->groupManager = $groupManager;
89
		$this->systemService = $systemService;
90
		$this->shareMapper = $shareMapper;
91
		$this->share = $share;
92
		$this->mailService = $mailService;
93
		$this->acl = $acl;
94
		$this->notificationService = $notificationService;
95
	}
96
97
	/**
98
	 * Read all shares of a poll based on the poll id and return list as array
99
	 *
100
	 * @return Share[]
101
	 *
102
	 * @psalm-return array<array-key, Share>
103
	 */
104
	public function list(int $pollId): array {
105
		try {
106
			$this->acl->setPollId($pollId)->request(Acl::PERMISSION_EDIT);
107
			$shares = $this->shareMapper->findByPoll($pollId);
108
		} catch (NotAuthorizedException $e) {
109
			return [];
110
		} catch (DoesNotExistException $e) {
111
			return [];
112
		}
113
114
		return $shares;
115
	}
116
117
	/**
118
	 * Validate share
119
	 */
120
	private function validate():void {
121
		switch ($this->share->getType()) {
122
			case Share::TYPE_PUBLIC:
123
				// public shares are alway valid
124
				break;
125
			case Share::TYPE_USER:
126
				if ($this->share->getUserId() !== $this->userId) {
127
					// share is not valid for user
128
					throw new NotAuthorizedException;
129
				}
130
				break;
131
			case Share::TYPE_GROUP:
132
				if (!\OC::$server->getUserSession()->isLoggedIn()) {
133
					throw new NotAuthorizedException;
134
				}
135
136
				if (!$this->groupManager->isInGroup($this->share->getUserId(), $this->userId)) {
137
					throw new NotAuthorizedException;
138
				}
139
140
				break;
141
			case Share::TYPE_EMAIL:
142
				break;
143
			case Share::TYPE_EXTERNAL:
144
				break;
145
			default:
146
				$this->logger->alert(json_encode('invalid share type ' . $this->share->getType()));
147
				throw new NotAuthorizedException;
148
		}
149
	}
150
151
	/**
152
	 * Get share by token
153
	 */
154
	public function get(string $token, bool $validate = false) {
155
		try {
156
			$this->share = $this->shareMapper->findByToken($token);
157
			if ($validate) {
158
				$this->validate();
159
			}
160
		} catch (DoesNotExistException $e) {
161
			throw new NotFoundException('Token ' . $token . ' does not exist');
162
		}
163
		// Allow users entering the poll with a public share access
164
165
		if ($this->share->getType() === Share::TYPE_PUBLIC && \OC::$server->getUserSession()->isLoggedIn()) {
166
			try {
167
				// Test if the user has already access.
168
				$this->acl->setPollId($this->share->getPollId())->request(Acl::PERMISSION_VIEW);
169
			} catch (NotAuthorizedException $e) {
170
				// If he is not authorized until now, create a new personal share for this user.
171
				// Return the created share
172
				return $this->create(
173
					$this->share->getPollId(),
174
					UserGroupClass::getUserGroupChild(Share::TYPE_USER, \OC::$server->getUserSession()->getUser()->getUID()),
175
					true
176
				);
177
			}
178
		}
179
		return $this->share;
180
	}
181
182
	/**
183
	 * Get share by token
184
	 *
185
	 * @return Share
186
	 */
187
	public function setInvitationSent(string $token): Share {
188
		$share = $this->shareMapper->findByToken($token);
189
		$share->setInvitationSent(time());
190
		return $this->shareMapper->update($share);
191
	}
192
193
	/**
194
	 * crate share - MUST BE PRIVATE!
195
	 *
196
	 * @return Share
197
	 */
198
	private function create(int $pollId, UserGroupClass $userGroup, bool $preventInvitation = false): Share {
199
		$preventInvitation = $userGroup->getType() === UserGroupClass::TYPE_PUBLIC ?: $preventInvitation;
200
		$token = \OC::$server->getSecureRandom()->generate(
201
			16,
202
			ISecureRandom::CHAR_DIGITS .
203
			ISecureRandom::CHAR_LOWER .
204
			ISecureRandom::CHAR_UPPER
205
		);
206
207
		$this->share = new Share();
208
		$this->share->setToken($token);
209
		$this->share->setPollId($pollId);
210
211
		// Convert user type contact to share type email
212
		if ($userGroup->getType() === UserGroupClass::TYPE_CONTACT) {
213
			$this->share->setType(Share::TYPE_EMAIL);
214
			$this->share->setUserId($userGroup->getEmailAddress());
215
		} else {
216
			$this->share->setType($userGroup->getType());
217
			$this->share->setUserId($userGroup->getType() === UserGroupClass::TYPE_PUBLIC ? $token : $userGroup->getPublicId());
218
		}
219
220
		$this->share->setInvitationSent($preventInvitation ? time() : 0);
221
		$this->share->setDisplayName($userGroup->getDisplayName());
222
		$this->share->setEmailAddress($userGroup->getEmailAddress());
223
224
		return $this->shareMapper->insert($this->share);
225
	}
226
227
	/**
228
	 * Add share
229
	 *
230
	 * @return Share
231
	 */
232
	public function add(int $pollId, string $type, string $userId = ''): Share {
233
		$this->acl->setPollId($pollId)->request(Acl::PERMISSION_EDIT);
234
235
		if ($type !== UserGroupClass::TYPE_PUBLIC) {
236
			try {
237
				$this->shareMapper->findByPollAndUser($pollId, $userId);
238
				throw new ShareAlreadyExistsException;
239
			} catch (MultipleObjectsReturnedException $e) {
240
				throw new ShareAlreadyExistsException;
241
			} catch (DoesNotExistException $e) {
242
				// continue
243
			}
244
		}
245
246
		$userGroup = UserGroupClass::getUserGroupChild($type, $userId);
247
		return $this->create($pollId, $userGroup);
248
	}
249
250
	/**
251
	 * Set emailAddress to personal share
252
	 * or update an email share with the username
253
	 *
254
	 * @return Share
255
	 */
256
	public function setEmailAddress(string $token, string $emailAddress, bool $emptyIsValid = false): Share {
257
		try {
258
			$this->share = $this->shareMapper->findByToken($token);
259
		} catch (DoesNotExistException $e) {
260
			throw new NotFoundException('Token ' . $token . ' does not exist');
261
		}
262
263
		if ($this->share->getType() === Share::TYPE_EXTERNAL) {
264
			$this->systemService->validateEmailAddress($emailAddress, $emptyIsValid);
265
			$this->share->setEmailAddress($emailAddress);
266
			// TODO: Send confirmation
267
			return $this->shareMapper->update($this->share);
268
		} else {
269
			throw new InvalidShareTypeException('Email address can only be set in external shares.');
270
		}
271
	}
272
273
	/**
274
	 * Delete emailAddress of personal share
275
	 *
276
	 * @return Share
277
	 */
278
	public function deleteEmailAddress(string $token): Share {
279
		try {
280
			$this->share = $this->shareMapper->findByToken($token);
281
		} catch (DoesNotExistException $e) {
282
			throw new NotFoundException('Token ' . $token . ' does not exist');
283
		}
284
285
		if ($this->share->getType() === Share::TYPE_EXTERNAL) {
286
			$this->share->setEmailAddress('');
287
			return $this->shareMapper->update($this->share);
288
		} else {
289
			throw new InvalidShareTypeException('Email address can only be set in external shares.');
290
		}
291
	}
292
293
	/**
294
	 * Create a personal share from a public share
295
	 * or update an email share with the username
296
	 *
297
	 * @return Share
298
	 */
299
	public function personal(string $token, string $userName, string $emailAddress = ''): Share {
300
		try {
301
			$this->share = $this->shareMapper->findByToken($token);
302
		} catch (DoesNotExistException $e) {
303
			throw new NotFoundException('Token ' . $token . ' does not exist');
304
		}
305
306
		$this->systemService->validatePublicUsername($userName, $token);
307
		$this->systemService->validateEmailAddress($emailAddress, true);
308
309
		if ($this->share->getType() === Share::TYPE_PUBLIC) {
310
			// Create new external share for user, who entered the poll via public link,
311
			// prevent invtation sending, when no email address is given
312
			$this->create(
313
				$this->share->getPollId(),
314
				UserGroupClass::getUserGroupChild(Share::TYPE_EXTERNAL, $userName, $userName, $emailAddress),
315
				!$emailAddress
316
			);
317
		} elseif ($this->share->getType() === Share::TYPE_EMAIL
318
				|| $this->share->getType() === Share::TYPE_CONTACT) {
319
			// Convert email and contact shares to external share, if user registers
320
			$this->share->setType(Share::TYPE_EXTERNAL);
321
			$this->share->setUserId($userName);
322
			$this->share->setDisplayName($userName);
323
324
			// prepare for resending inviataion to new email address
325
			if ($emailAddress !== $this->share->getEmailAddress()) {
326
				$this->share->setInvitationSent(0);
327
			}
328
			$this->share->setEmailAddress($emailAddress);
329
			$this->shareMapper->update($this->share);
330
		} else {
331
			throw new NotAuthorizedException;
332
		}
333
334
		// send invitaitoin mail, if invitationSent has no timestamp
335
		if (!$this->share->getInvitationSent()) {
336
			$this->mailService->resendInvitation($this->share->getToken());
337
		}
338
339
		return $this->share;
340
	}
341
342
	/**
343
	 * Delete share
344
	 */
345
	public function delete(string $token): string {
346
		try {
347
			$this->share = $this->shareMapper->findByToken($token);
348
			$this->acl->setPollId($this->share->getPollId())->request(Acl::PERMISSION_EDIT);
349
			$this->shareMapper->delete($this->share);
350
		} catch (DoesNotExistException $e) {
351
			// silently catch
352
		}
353
		return $token;
354
	}
355
356
	/**
357
	 * Sent invitation mails for a share
358
	 * Additionally send notification via notifications
359
	 */
360
	public function sendInvitation(string $token): array {
361
		$share = $this->get($token);
362
		if ($share->getType() === Share::TYPE_USER) {
363
			$this->notificationService->sendInvitation($share->getPollId(), $share->getUserId());
364
365
			// TODO: skip this atm, to send invitations as mail too, if user is a site user
366
			// $sentResult = ['sentMails' => [new User($share->getuserId())]];
367
			// $this->shareService->setInvitationSent($token);
368
		} elseif ($share->getType() === Share::TYPE_GROUP) {
369
			foreach ($share->getMembers() as $member) {
370
				$this->notificationService->sendInvitation($share->getPollId(), $member->getId());
371
			}
372
		}
373
374
		return $this->mailService->sendInvitation($token);
375
	}
376
}
377