Completed
Pull Request — master (#551)
by Maxence
02:42
created

RemoteController::extractDataFromFromRequest()   A

Complexity

Conditions 5
Paths 25

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 9.1128
c 0
b 0
f 0
cc 5
nc 25
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2020
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
32
namespace OCA\Circles\Controller;
33
34
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
35
use daita\MySmallPhpTools\Exceptions\InvalidOriginException;
36
use daita\MySmallPhpTools\Exceptions\ItemNotFoundException;
37
use daita\MySmallPhpTools\Exceptions\MalformedArrayException;
38
use daita\MySmallPhpTools\Exceptions\SignatoryException;
39
use daita\MySmallPhpTools\Exceptions\SignatureException;
40
use daita\MySmallPhpTools\Exceptions\UnknownTypeException;
41
use daita\MySmallPhpTools\Model\Nextcloud\nc21\NC21SignedRequest;
42
use daita\MySmallPhpTools\Model\SimpleDataStore;
43
use daita\MySmallPhpTools\Traits\Nextcloud\nc21\TNC21Controller;
44
use Exception;
45
use OCA\Circles\Db\CircleRequest;
46
use OCA\Circles\Exceptions\CircleNotFoundException;
47
use OCA\Circles\Exceptions\FederatedEventDSyncException;
48
use OCA\Circles\Exceptions\FederatedItemException;
49
use OCA\Circles\Model\Federated\FederatedEvent;
50
use OCA\Circles\Model\Federated\RemoteInstance;
51
use OCA\Circles\Model\FederatedUser;
52
use OCA\Circles\Model\Member;
53
use OCA\Circles\Service\CircleService;
54
use OCA\Circles\Service\ConfigService;
55
use OCA\Circles\Service\FederatedUserService;
56
use OCA\Circles\Service\MemberService;
57
use OCA\Circles\Service\RemoteDownstreamService;
58
use OCA\Circles\Service\RemoteStreamService;
59
use OCP\AppFramework\Controller;
60
use OCP\AppFramework\Http;
61
use OCP\AppFramework\Http\DataResponse;
62
use OCP\IRequest;
63
64
65
/**
66
 * Class RemoteController
67
 *
68
 * @package OCA\Circles\Controller
69
 */
70
class RemoteController extends Controller {
71
72
73
	use TNC21Controller;
74
75
76
	/** @var CircleRequest */
77
	private $circleRequest;
78
79
	/** @var RemoteStreamService */
80
	private $remoteStreamService;
81
82
	/** @var RemoteDownstreamService */
83
	private $remoteDownstreamService;
84
85
	/** @var FederatedUserService */
86
	private $federatedUserService;
87
88
	/** @var CircleService */
89
	private $circleService;
90
91
	/** @var MemberService */
92
	private $memberService;
93
94
	/** @var ConfigService */
95
	private $configService;
96
97
98
	/**
99
	 * RemoteController constructor.
100
	 *
101
	 * @param string $appName
102
	 * @param IRequest $request
103
	 * @param CircleRequest $circleRequest
104
	 * @param RemoteStreamService $remoteStreamService
105
	 * @param RemoteDownstreamService $remoteDownstreamService
106
	 * @param FederatedUserService $federatedUserService
107
	 * @param CircleService $circleService
108
	 * @param MemberService $memberService
109
	 * @param ConfigService $configService
110
	 */
111
	public function __construct(
112
		string $appName, IRequest $request, CircleRequest $circleRequest,
113
		RemoteStreamService $remoteStreamService,
114
		RemoteDownstreamService $remoteDownstreamService, FederatedUserService $federatedUserService,
115
		CircleService $circleService, MemberService $memberService, ConfigService $configService
116
	) {
117
		parent::__construct($appName, $request);
118
		$this->circleRequest = $circleRequest;
119
		$this->remoteStreamService = $remoteStreamService;
120
		$this->remoteDownstreamService = $remoteDownstreamService;
121
		$this->federatedUserService = $federatedUserService;
122
		$this->circleService = $circleService;
123
		$this->memberService = $memberService;
124
		$this->configService = $configService;
125
126
		$this->setup('app', 'circles');
127
	}
128
129
130
	/**
131
	 * @PublicPage
132
	 * @NoCSRFRequired
133
	 *
134
	 * @return DataResponse
135
	 */
136
	public function event(): DataResponse {
137
		try {
138
			$event = $this->extractEventFromRequest();
139
		} catch (Exception $e) {
140
			return $this->fail($e, [], Http::STATUS_BAD_REQUEST);
141
		}
142
143
		try {
144
			$this->remoteDownstreamService->requestedEvent($event);
145
		} catch (FederatedItemException $e) {
146
			$this->e($e, ['event' => $event]);
147
			$event->setReadingOutcome($e->getMessage(), $e->getParams(), true);
148
		} catch (FederatedEventDSyncException $e) {
149
			return $this->fail($e, [], Http::STATUS_CONFLICT);
150
		} catch (Exception $e) {
151
			$this->e($e);
152
153
			return $this->fail($e, [], Http::STATUS_BAD_REQUEST);
154
		}
155
156
		return $this->successObj($event->getOutcome());
157
	}
158
159
160
	/**
161
	 * @PublicPage
162
	 * @NoCSRFRequired
163
	 *
164
	 * @return DataResponse
165
	 */
166
	public function incoming(): DataResponse {
167
		try {
168
			$event = $this->extractEventFromRequest();
169
170
			$result = $this->remoteDownstreamService->incomingEvent($event);
171
172
			return $this->success($result);
173
		} catch (Exception $e) {
174
			return $this->fail($e);
175
		}
176
	}
177
178
179
	/**
180
	 * @PublicPage
181
	 * @NoCSRFRequired
182
	 *
183
	 * @return DataResponse
184
	 * @throws InvalidOriginException
185
	 * @throws MalformedArrayException
186
	 * @throws SignatoryException
187
	 * @throws SignatureException
188
	 */
189
	public function test(): DataResponse {
190
		$test = $this->remoteStreamService->incomingSignedRequest($this->configService->getLocalInstance());
191
192
		return $this->successObj($test);
193
	}
194
195
196
	/**
197
	 * @PublicPage
198
	 * @NoCSRFRequired
199
	 *
200
	 * @return DataResponse
201
	 */
202
	public function circles(): DataResponse {
203
		try {
204
			$data = $this->extractDataFromFromRequest();
205
206
			/** @var Member $filter */
207
			$filter = $data->gObj('filter');
208
			$circles = $this->circleService->getCircles($filter);
209
210
			return $this->success($circles, false);
211
		} catch (Exception $e) {
212
			return $this->fail($e);
213
		}
214
	}
215
216
217
	/**
218
	 * @PublicPage
219
	 * @NoCSRFRequired
220
	 *
221
	 * @param string $circleId
222
	 *
223
	 * @return DataResponse
224
	 */
225
	public function circle(string $circleId): DataResponse {
226
		try {
227
			$this->extractDataFromFromRequest();
228
			$circle = $this->circleService->getCircle($circleId);
229
230
			return $this->successObj($circle);
231
		} catch (CircleNotFoundException $e) {
232
			return $this->success([], false);
233
		} catch (Exception $e) {
234
			return $this->fail($e);
235
		}
236
	}
237
238
239
	/**
240
	 * @PublicPage
241
	 * @NoCSRFRequired
242
	 *
243
	 * @param string $circleId
244
	 *
245
	 * @return DataResponse
246
	 */
247
	public function members(string $circleId): DataResponse {
248
		try {
249
			$this->extractDataFromFromRequest();
250
			$members = $this->memberService->getMembers($circleId);
251
252
			return $this->success($members, false);
253
		} catch (Exception $e) {
254
			return $this->fail($e);
255
		}
256
	}
257
258
259
	/**
260
	 * @PublicPage
261
	 * @NoCSRFRequired
262
	 *
263
	 * @param string $type
264
	 * @param string $userId
265
	 *
266
	 * @return DataResponse
267
	 */
268
	public function member(string $type, string $userId): DataResponse {
269
		try {
270
			$signed =
271
				$this->remoteStreamService->incomingSignedRequest($this->configService->getLocalInstance());
272
			$remoteInstance = $this->confirmRemoteInstance($signed);
0 ignored issues
show
Unused Code introduced by
$remoteInstance is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
273
274
//			$this->federatedUserService->setRemoteInstance($remoteInstance);
275
//			$this->federatedUserService->bypassCurrentUserCondition(true);
276
277
			$federatedUser = $this->federatedUserService->createLocalFederatedUser($userId);
278
279
			return $this->successObj($federatedUser);
280
		} catch (Exception $e) {
281
			return $this->fail($e);
282
		}
283
	}
284
285
286
	/**
287
	 * @param NC21SignedRequest $signedRequest
288
	 *
289
	 * @return RemoteInstance
290
	 * @throws SignatoryException
291
	 */
292
	private function confirmRemoteInstance(NC21SignedRequest $signedRequest): RemoteInstance {
293
		/** @var RemoteInstance $signatory */
294
		$signatory = $signedRequest->getSignatory();
295
296
		if (!$signatory instanceof RemoteInstance) {
297
			$this->debug('Signatory is not a known RemoteInstance', ['signedRequest' => $signedRequest]);
298
			throw new SignatoryException('Could not confirm identity');
299
		}
300
301
		if (!$this->configService->isLocalInstance($signedRequest->getOrigin())
302
			&& $signatory->getType() === RemoteInstance::TYPE_UNKNOWN) {
303
			$this->debug('Could not confirm identity', ['signedRequest' => $signedRequest]);
304
			throw new SignatoryException('Could not confirm identity');
305
		}
306
307
		return $signatory;
308
	}
309
310
311
	/**
312
	 * @return FederatedEvent
313
	 * @throws InvalidOriginException
314
	 * @throws MalformedArrayException
315
	 * @throws SignatoryException
316
	 * @throws SignatureException
317
	 * @throws InvalidItemException
318
	 */
319
	private function extractEventFromRequest(): FederatedEvent {
320
		$signed = $this->remoteStreamService->incomingSignedRequest($this->configService->getLocalInstance());
321
		$this->confirmRemoteInstance($signed);
322
323
		$event = new FederatedEvent();
324
		$event->import(json_decode($signed->getBody(), true));
325
		$event->setIncomingOrigin($signed->getOrigin());
326
327
		return $event;
328
	}
329
330
331
	/**
332
	 * @throws CircleNotFoundException
333
	 * @throws InvalidOriginException
334
	 * @throws MalformedArrayException
335
	 * @throws SignatoryException
336
	 * @throws SignatureException
337
	 * @throws UnknownTypeException
338
	 */
339
	private function extractDataFromFromRequest(): SimpleDataStore {
340
		$signed = $this->remoteStreamService->incomingSignedRequest($this->configService->getLocalInstance());
341
		$remoteInstance = $this->confirmRemoteInstance($signed);
342
343
		$data = new SimpleDataStore();
344
		$store = new SimpleDataStore(json_decode($signed->getBody(), true));
345
		try {
346
			/** @var FederatedUser $initiator */
347
			$initiator = $store->gObj('initiator', FederatedUser::class);
348
			if (is_null($initiator)) {
349
				throw new InvalidItemException();
350
			}
351
			$this->federatedUserService->setCurrentUser($initiator);
352
		} catch (InvalidItemException | ItemNotFoundException $e) {
353
			$this->federatedUserService->bypassCurrentUserCondition(true);
354
		}
355
356
		try {
357
			/** @var FederatedUser $initiator */
358
			$filter = $store->gObj('filter', Member::class);
359
			if (is_null($filter)) {
360
				throw new InvalidItemException();
361
			}
362
			$data->aObj('filter', $filter);
363
		} catch (InvalidItemException | ItemNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
364
		}
365
366
		$this->federatedUserService->setRemoteInstance($remoteInstance);
367
368
		return $data;
369
	}
370
371
}
372
373