Completed
Push — master ( 574ca3...69f17d )
by Maxence
04:30 queued 02:09
created

GlobalScaleService::asyncBroadcast()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 9.52
c 0
b 0
f 0
cc 3
nc 4
nop 1
1
<?php declare(strict_types=1);
2
3
4
/**
5
 * Circles - Bring cloud-users closer together.
6
 *
7
 * This file is licensed under the Affero General Public License version 3 or
8
 * later. See the COPYING file.
9
 *
10
 * @author Maxence Lange <[email protected]>
11
 * @copyright 2019
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
30
namespace OCA\Circles\Service;
31
32
33
use daita\MySmallPhpTools\Exceptions\RequestContentException;
34
use daita\MySmallPhpTools\Exceptions\RequestNetworkException;
35
use daita\MySmallPhpTools\Exceptions\RequestResultNotJsonException;
36
use daita\MySmallPhpTools\Exceptions\RequestResultSizeException;
37
use daita\MySmallPhpTools\Exceptions\RequestServerException;
38
use daita\MySmallPhpTools\Model\Nextcloud\NC19Request;
39
use daita\MySmallPhpTools\Model\Request;
40
use daita\MySmallPhpTools\Traits\Nextcloud\TNC19Request;
41
use daita\MySmallPhpTools\Traits\TStringTools;
42
use OC;
43
use OC\Security\IdentityProof\Signer;
44
use OC\User\NoUserException;
45
use OCA\Circles\Db\GSEventsRequest;
46
use OCA\Circles\Exceptions\GlobalScaleEventException;
47
use OCA\Circles\Exceptions\GSKeyException;
48
use OCA\Circles\Exceptions\GSStatusException;
49
use OCA\Circles\GlobalScale\AGlobalScaleEvent;
50
use OCA\Circles\Model\GlobalScale\GSEvent;
51
use OCA\Circles\Model\GlobalScale\GSWrapper;
52
use OCP\AppFramework\QueryException;
53
use OCP\IURLGenerator;
54
use OCP\IUser;
55
use OCP\IUserManager;
56
use OCP\IUserSession;
57
58
59
/**
60
 * Class GlobalScaleService
61
 *
62
 * @package OCA\Circles\Service
63
 */
64
class GlobalScaleService {
65
66
67
	use TNC19Request;
68
	use TStringTools;
69
70
71
	/** @var IURLGenerator */
72
	private $urlGenerator;
73
74
	/** @var IUserManager */
75
	private $userManager;
76
77
	/** @var IUserSession */
78
	private $userSession;
79
80
	/** @var Signer */
81
	private $signer;
82
83
	/** @var GSEventsRequest */
84
	private $gsEventsRequest;
85
86
	/** @var ConfigService */
87
	private $configService;
88
89
	/** @var MiscService */
90
	private $miscService;
91
92
93
	/**
94
	 * GlobalScaleService constructor.
95
	 *
96
	 * @param IURLGenerator $urlGenerator
97
	 * @param IUserManager $userManager
98
	 * @param IUserSession $userSession
99
	 * @param Signer $signer
100
	 * @param GSEventsRequest $gsEventsRequest
101
	 * @param ConfigService $configService
102
	 * @param MiscService $miscService
103
	 */
104
	public function __construct(
105
		IURLGenerator $urlGenerator,
106
		IUserManager $userManager,
107
		IUserSession $userSession,
108
		Signer $signer,
109
		GSEventsRequest $gsEventsRequest,
110
		ConfigService $configService,
111
		MiscService $miscService
112
	) {
113
		$this->urlGenerator = $urlGenerator;
114
		$this->userManager = $userManager;
115
		$this->userSession = $userSession;
116
		$this->signer = $signer;
117
		$this->gsEventsRequest = $gsEventsRequest;
118
		$this->configService = $configService;
119
		$this->miscService = $miscService;
120
	}
121
122
123
	/**
124
	 * @param GSEvent $event
125
	 *
126
	 * @return string
127
	 */
128
	public function asyncBroadcast(GSEvent $event): string {
129
		$wrapper = new GSWrapper();
130
		$wrapper->setEvent($event);
131
		$wrapper->setToken($this->uuid());
132
		$wrapper->setCreation(time());
133
		$wrapper->setSeverity($event->getSeverity());
134
135
		foreach ($this->getInstances($event->isAsync()) as $instance) {
136
			$wrapper->setInstance($instance);
137
			$wrapper = $this->gsEventsRequest->create($wrapper);
138
		}
139
140
		$request = new NC19Request('', Request::TYPE_POST);
141
		$this->configService->configureRequest(
142
			$request, 'circles.GlobalScale.asyncBroadcast', ['token' => $wrapper->getToken()]
143
		);
144
145
		try {
146
			$this->doRequest($request);
147
		} catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException $e) {
148
			$this->miscService->e($e);
149
		}
150
151
		return $wrapper->getToken();
152
	}
153
154
155
	/**
156
	 * @param GSEvent $event
157
	 *
158
	 * @return AGlobalScaleEvent
159
	 * @throws GlobalScaleEventException
160
	 */
161
	public function getGlobalScaleEvent(GSEvent $event): AGlobalScaleEvent {
162
		$class = $this->getClassNameFromEvent($event);
163
		try {
164
			$gs = OC::$server->query($class);
165
			if (!$gs instanceof AGlobalScaleEvent) {
166
				throw new GlobalScaleEventException($class . ' not an AGlobalScaleEvent');
167
			}
168
169
			return $gs;
170
		} catch (QueryException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\AppFramework\QueryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
171
			throw new GlobalScaleEventException('AGlobalScaleEvent ' . $class . ' not found');
172
		}
173
	}
174
175
176
	/**
177
	 * @return string
178
	 */
179
	public function getKey(): string {
180
		try {
181
			$key = $this->configService->getGSStatus(ConfigService::GS_KEY);
182
		} catch (GSStatusException $e) {
183
			$key = $this->configService->getAppValue(ConfigService::CIRCLES_LOCAL_GSKEY);
184
			if ($key === '') {
185
				$key = $this->token(31);
186
				$this->configService->setAppValue(ConfigService::CIRCLES_LOCAL_GSKEY, $key);
187
			}
188
		}
189
190
		return md5('gskey:' . $key);
191
	}
192
193
	/**
194
	 * @param string $key
195
	 *
196
	 * @throws GSKeyException
197
	 */
198
	public function checkKey(string $key) {
199
		if ($key !== $this->getKey()) {
200
			throw new GSKeyException('invalid key');
201
		}
202
	}
203
204
205
	/**
206
	 * @param GSEvent $event
207
	 *
208
	 * @throws GSKeyException
209
	 */
210
	public function checkEvent(GSEvent $event): void {
211
		$this->checkKey($event->getKey());
212
	}
213
214
215
	/**
216
	 * @param bool $all
217
	 *
218
	 * @return array
219
	 */
220
	public function getInstances(bool $all = false): array {
221
		/** @var string $lookup */
222
		try {
223
			$lookup = $this->configService->getGSStatus(ConfigService::GS_LOOKUP);
224
			$request = new NC19Request(ConfigService::GS_LOOKUP_INSTANCES, Request::TYPE_POST);
225
			$request->setProtocols(['https', 'http']);
226
			$request->basedOnUrl($lookup);
227
			$this->configService->configureRequest($request);
228
			$request->addData('authKey', $this->configService->getGSStatus(ConfigService::GS_KEY));
229
230
			try {
231
				$instances = $this->retrieveJson($request);
232
			} catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException | RequestResultNotJsonException $e) {
233
				$this->miscService->log(
234
					'Issue while retrieving instances from lookup: ' . get_class($e) . ' ' . $e->getMessage()
235
				);
236
237
				return [];
238
			}
239
		} catch (GSStatusException $e) {
240
			if (!$all) {
241
				return [];
242
			}
243
244
			return [$this->configService->getLocalInstance()];
245
		}
246
247
		if ($all) {
248
			return $instances;
249
		}
250
251
		return array_values(array_diff($instances, $this->configService->getTrustedDomains()));
252
	}
253
254
255
256
	/**
257
	 * @param GSEvent $event
258
	 *
259
	 * @return string
260
	 * @throws GlobalScaleEventException
261
	 */
262
	private function getClassNameFromEvent(GSEvent $event): string {
263
		$className = $event->getType();
264
		if (substr($className, 0, 25) !== '\OCA\Circles\GlobalScale\\' || strpos($className, '.')) {
265
			throw new GlobalScaleEventException(
266
				$className . ' does not seems to be a secured AGlobalScaleEvent'
267
			);
268
		}
269
270
		return $className;
271
	}
272
273
274
	/**
275
	 * @return IUser
276
	 * @throws NoUserException
277
	 */
278
	private function getRandomUser(): IUser {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
279
		$user = $this->userSession->getUser();
280
		if ($user !== null) {
281
			return $user;
282
		}
283
284
		$random = $this->userManager->search('', 1);
285
		if (sizeof($random) > 0) {
286
			return array_shift($random);
287
		}
288
289
		throw new NoUserException();
290
	}
291
292
}
293
294