Completed
Push — master ( 1eadc9...2741a1 )
by Maxence
14s queued 11s
created

GlobalScaleService::getInstances()   A

Complexity

Conditions 4
Paths 19

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 9.472
c 0
b 0
f 0
cc 4
nc 19
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\Request;
39
use daita\MySmallPhpTools\Traits\TRequest;
40
use daita\MySmallPhpTools\Traits\TStringTools;
41
use OC;
42
use OC\Security\IdentityProof\Signer;
43
use OC\User\NoUserException;
44
use OCA\Circles\Db\GSEventsRequest;
45
use OCA\Circles\Exceptions\GlobalScaleEventException;
46
use OCA\Circles\Exceptions\GSKeyException;
47
use OCA\Circles\Exceptions\GSStatusException;
48
use OCA\Circles\GlobalScale\AGlobalScaleEvent;
49
use OCA\Circles\Model\GlobalScale\GSEvent;
50
use OCA\Circles\Model\GlobalScale\GSWrapper;
51
use OCP\AppFramework\QueryException;
52
use OCP\IURLGenerator;
53
use OCP\IUser;
54
use OCP\IUserManager;
55
use OCP\IUserSession;
56
57
58
/**
59
 * Class GlobalScaleService
60
 *
61
 * @package OCA\Circles\Service
62
 */
63
class GlobalScaleService {
64
65
66
	use TRequest;
67
	use TStringTools;
68
69
70
	/** @var IURLGenerator */
71
	private $urlGenerator;
72
73
	/** @var IUserManager */
74
	private $userManager;
75
76
	/** @var IUserSession */
77
	private $userSession;
78
79
	/** @var Signer */
80
	private $signer;
81
82
	/** @var GSEventsRequest */
83
	private $gsEventsRequest;
84
85
	/** @var ConfigService */
86
	private $configService;
87
88
	/** @var MiscService */
89
	private $miscService;
90
91
92
	/**
93
	 * GlobalScaleService constructor.
94
	 *
95
	 * @param IURLGenerator $urlGenerator
96
	 * @param IUserManager $userManager
97
	 * @param IUserSession $userSession
98
	 * @param Signer $signer
99
	 * @param GSEventsRequest $gsEventsRequest
100
	 * @param ConfigService $configService
101
	 * @param MiscService $miscService
102
	 */
103
	public function __construct(
104
		IURLGenerator $urlGenerator,
105
		IUserManager $userManager,
106
		IUserSession $userSession,
107
		Signer $signer,
108
		GSEventsRequest $gsEventsRequest,
109
		ConfigService $configService,
110
		MiscService $miscService
111
	) {
112
		$this->urlGenerator = $urlGenerator;
113
		$this->userManager = $userManager;
114
		$this->userSession = $userSession;
115
		$this->signer = $signer;
116
		$this->gsEventsRequest = $gsEventsRequest;
117
		$this->configService = $configService;
118
		$this->miscService = $miscService;
119
	}
120
121
122
	/**
123
	 * @param GSEvent $event
124
	 */
125
	public function asyncBroadcast(GSEvent $event): void {
126
		$wrapper = new GSWrapper();
127
		$wrapper->setEvent($event);
128
		$wrapper->setToken($this->uuid());
129
		$wrapper->setCreation(time());
130
		$wrapper->setSeverity($event->getSeverity());
131
132
		foreach ($this->getInstances($event->isAsync()) as $instance) {
133
			$wrapper->setInstance($instance);
134
			$wrapper = $this->gsEventsRequest->create($wrapper);
135
		}
136
137
		$path = $this->urlGenerator->linkToRoute(
138
			'circles.GlobalScale.asyncBroadcast', ['token' => $wrapper->getToken()]
139
		);
140
141
		$request = new Request($path, Request::TYPE_PUT);
142
143
		$baseUrl = $this->urlGenerator->getBaseUrl();
144
		if (substr($baseUrl, 0, 16) === 'http://localhost') {
145
			$request->setBaseUrl(substr($baseUrl, 16));
0 ignored issues
show
Bug introduced by
The method setBaseUrl() does not seem to exist on object<daita\MySmallPhpTools\Model\Request>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
146
			$request->setAddress($this->configService->getLocalCloudId());
147
			$request->setProtocols(['https', 'http']);
0 ignored issues
show
Bug introduced by
The method setProtocols() does not exist on daita\MySmallPhpTools\Model\Request. Did you maybe mean setProtocol()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
148
		} else {
149
			$request->setAddressFromUrl($baseUrl);
150
		}
151
152
		try {
153
			$this->doRequest($request);
154
		} catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
155
		}
156
	}
157
158
159
	/**
160
	 * @param GSEvent $event
161
	 *
162
	 * @return AGlobalScaleEvent
163
	 * @throws GlobalScaleEventException
164
	 */
165
	public function getGlobalScaleEvent(GSEvent $event): AGlobalScaleEvent {
166
		$class = $this->getClassNameFromEvent($event);
167
		try {
168
			$gs = OC::$server->query($class);
169
			if (!$gs instanceof AGlobalScaleEvent) {
170
				throw new GlobalScaleEventException($class . ' not an AGlobalScaleEvent');
171
			}
172
173
			return $gs;
174
		} 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...
175
			throw new GlobalScaleEventException('AGlobalScaleEvent ' . $class . ' not found');
176
		}
177
	}
178
179
180
	/**
181
	 * @return string
182
	 */
183
	public function getKey(): string {
184
		// TODO: sign event with real and temp key.
185
		return 'abcd';
186
	}
187
188
189
	/**
190
	 * @param string $key
191
	 *
192
	 * @throws GSKeyException
193
	 */
194
	public function checkKey(string $key) {
195
		if ($key !== $this->getKey()) {
196
			throw new GSKeyException('invalid key');
197
		}
198
	}
199
200
201
	/**
202
	 * @param GSEvent $event
203
	 *
204
	 * @throws GSKeyException
205
	 */
206
	public function checkEvent(GSEvent $event): void {
207
		$this->checkKey($event->getKey());
208
	}
209
210
211
	/**
212
	 * @param bool $all
213
	 *
214
	 * @return array
215
	 */
216
	public function getInstances(bool $all = false): array {
217
		/** @var string $lookup */
218
		try {
219
			$lookup = $this->configService->getGSStatus(ConfigService::GS_LOOKUP);
220
			$request = new Request(ConfigService::GS_LOOKUP_INSTANCES, Request::TYPE_POST);
221
222
			$user = $this->getRandomUser();
223
			$data = $this->signer->sign('lookupserver', ['federationId' => $user->getCloudId()], $user);
224
			$request->setData($data);
225
			$request->setAddressFromUrl($lookup);
226
227
			try {
228
				$instances = $this->retrieveJson($request);
229
			} catch (RequestContentException | RequestNetworkException | RequestResultSizeException | RequestServerException | RequestResultNotJsonException $e) {
230
				$this->miscService->log('Issue while retrieving instances from lookup: ' . $e->getMessage());
231
232
				return [];
233
			}
234
		} catch (NoUserException | GSStatusException $e) {
235
			$instances = [$this->configService->getLocalCloudId()];
236
		}
237
238
		if ($all) {
239
			return $instances;
240
		}
241
242
		return array_values(array_diff($instances, $this->configService->getTrustedDomains()));
243
	}
244
245
246
	/**
247
	 * @param GSEvent $event
248
	 *
249
	 * @return string
250
	 * @throws GlobalScaleEventException
251
	 */
252
	private function getClassNameFromEvent(GSEvent $event): string {
253
		$className = $event->getType();
254
		if (substr($className, 0, 25) !== '\OCA\Circles\GlobalScale\\' || strpos($className, '.')) {
255
			throw new GlobalScaleEventException(
256
				$className . ' does not seems to be a secured AGlobalScaleEvent'
257
			);
258
		}
259
260
		return $className;
261
	}
262
263
264
	/**
265
	 * @return IUser
266
	 * @throws NoUserException
267
	 */
268
	private function getRandomUser(): IUser {
269
		$user = $this->userSession->getUser();
270
		if ($user !== null) {
271
			return $user;
272
		}
273
274
		$random = $this->userManager->search('', 1);
275
		if (sizeof($random) > 0) {
276
			return array_shift($random);
277
		}
278
279
		throw  new NoUserException();
280
	}
281
282
}
283
284