Completed
Push — master ( 77c8bc...4a5e09 )
by Maxence
02:57
created

FederatedController::federatedSuccess()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 12
rs 9.4285
cc 3
eloc 6
nc 4
nop 2
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
namespace OCA\Circles\Controller;
28
29
use Exception;
30
use OC\AppFramework\Http;
31
use OCA\Circles\Api\v1\Circles;
32
use OCA\Circles\Exceptions\CircleDoesNotExistException;
33
use OCA\Circles\Exceptions\SharingFrameAlreadyExistException;
34
use OCA\Circles\Model\FederatedLink;
35
use OCA\Circles\Model\SharingFrame;
36
use OCA\Circles\Service\BroadcastService;
37
use OCA\Circles\Service\CirclesService;
38
use OCA\Circles\Service\ConfigService;
39
use OCA\Circles\Service\FederatedLinkCreationService;
40
use OCA\Circles\Service\FederatedLinkService;
41
use OCA\Circles\Service\MembersService;
42
use OCA\Circles\Service\MiscService;
43
use OCA\Circles\Service\SharingFrameService;
44
use OCP\AppFramework\Controller;
45
use OCP\AppFramework\Http\DataResponse;
46
use OCP\IL10N;
47
use OCP\IRequest;
48
49
class FederatedController extends Controller {
50
51
	/** @var string */
52
	protected $userId;
53
54
	/** @var IL10N */
55
	protected $l10n;
56
57
	/** @var ConfigService */
58
	protected $configService;
59
60
	/** @var CirclesService */
61
	protected $circlesService;
62
63
	/** @var MembersService */
64
	protected $membersService;
65
66
	/** @var SharingFrameService */
67
	protected $sharingFrameService;
68
69
	/** @var BroadcastService */
70
	protected $broadcastService;
71
72
	/** @var FederatedLinkService */
73
	protected $federatedLinkService;
74
75
	/** @var FederatedLinkCreationService */
76
	protected $federatedLinkCreationService;
77
78
	/** @var MiscService */
79
	protected $miscService;
80
81
82
	/**
83
	 * BaseController constructor.
84
	 *
85
	 * @param string $appName
86
	 * @param IRequest $request
87
	 * @param string $UserId
88
	 * @param IL10N $l10n
89
	 * @param ConfigService $configService
90
	 * @param CirclesService $circlesService
91
	 * @param SharingFrameService $sharingFrameService
92
	 * @param BroadcastService $broadcastService
93
	 * @param FederatedLinkService $federatedLinkService
94
	 * @param FederatedLinkCreationService $federatedLinkCreationService
95
	 * @param MiscService $miscService
96
	 */
97 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style Naming introduced by
The parameter $UserId is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
98
		$appName,
99
		IRequest $request,
100
		$UserId,
101
		IL10N $l10n,
102
		ConfigService $configService,
103
		CirclesService $circlesService,
104
		SharingFrameService $sharingFrameService,
105
		BroadcastService $broadcastService,
106
		FederatedLinkService $federatedLinkService,
107
		FederatedLinkCreationService $federatedLinkCreationService,
108
		MiscService $miscService
109
	) {
110
		parent::__construct($appName, $request);
111
112
		$this->userId = $UserId;
113
		$this->l10n = $l10n;
114
		$this->configService = $configService;
115
		$this->circlesService = $circlesService;
116
		$this->sharingFrameService = $sharingFrameService;
117
		$this->broadcastService = $broadcastService;
118
		$this->federatedLinkService = $federatedLinkService;
119
		$this->federatedLinkCreationService = $federatedLinkCreationService;
120
		$this->miscService = $miscService;
121
	}
122
123
124
	/**
125
	 * requestedLink()
126
	 *
127
	 * Called when a remote circle want to create a link.
128
	 * The function check if it is possible first; then create a link-object
129
	 * and sent it to be saved in the database.
130
	 *
131
	 * @PublicPage
132
	 * @NoCSRFRequired
133
	 *
134
	 * @param $data
135
	 *
136
	 * @return DataResponse
137
	 */
138
	public function requestedLink($data) {
139 View Code Duplication
		if (MiscService::get($data, 'uniqueId') === ''
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
140
			|| !$this->configService->isFederatedCirclesAllowed()) {
141
			return $this->federatedFail('federated_not_allowed');
142
		}
143
144
		try {
145
			Circles::compareVersion(MiscService::get($data, 'apiVersion'));
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...et($data, 'apiVersion') targeting OCA\Circles\Service\MiscService::get() can also be of type string; however, OCA\Circles\Api\v1\Circles::compareVersion() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
146
			$circle = $this->circlesService->infoCircleByName(MiscService::get($data, 'linkTo'));
147
			$link = $this->generateNewLink($data);
148
149
			$this->federatedLinkCreationService->requestedLinkFromRemoteCircle($circle, $link);
0 ignored issues
show
Bug introduced by
It seems like $circle defined by $this->circlesService->i...::get($data, 'linkTo')) on line 146 can be null; however, OCA\Circles\Service\Fede...dLinkFromRemoteCircle() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
150
151
			return $this->federatedSuccess(
152
				['status' => $link->getStatus(), 'uniqueId' => $circle->getUniqueId(true)], $link
153
			);
154
		} catch (CircleDoesNotExistException $e) {
155
			return $this->federatedFail('circle_does_not_exist');
156
		} catch (Exception $e) {
157
			return $this->federatedFail($e->getMessage());
158
		}
159
	}
160
161
162
	/**
163
	 * @param $data
164
	 *
165
	 * @return FederatedLink
166
	 */
167
	private function generateNewLink($data) {
168
		MiscService::mustContains($data, ['token', 'uniqueId', 'sourceName', 'address']);
169
		$link = new FederatedLink();
170
171
		$link->setToken(MiscService::get($data, 'token'))
172
			 ->setUniqueId(MiscService::get($data, 'uniqueId'))
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...:get($data, 'uniqueId') targeting OCA\Circles\Service\MiscService::get() can also be of type array; however, OCA\Circles\Model\FederatedLink::setUniqueId() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
173
			 ->setRemoteCircleName(MiscService::get($data, 'sourceName'))
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...et($data, 'sourceName') targeting OCA\Circles\Service\MiscService::get() can also be of type array; however, OCA\Circles\Model\Federa...::setRemoteCircleName() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
174
			 ->setAddress(MiscService::get($data, 'address'));
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...::get($data, 'address') targeting OCA\Circles\Service\MiscService::get() can also be of type array; however, OCA\Circles\Model\FederatedLink::setAddress() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
175
176
		return $link;
177
	}
178
179
	/**
180
	 * receiveFederatedDelivery()
181
	 *
182
	 * Note: this function will close the request mid-run from the client but will still
183
	 * running its process.
184
	 *
185
	 * Called by a remote circle to broadcast a Share item, the function will save the item
186
	 * in the database and broadcast it locally. A status response is sent to the remote to free
187
	 * the remote process before starting to broadcast the item to other federated links.
188
	 *
189
	 * @PublicPage
190
	 * @NoCSRFRequired
191
	 *
192
	 * @param array $apiVersion
193
	 * @param string $token
194
	 * @param string $uniqueId
195
	 * @param string $item
196
	 *
197
	 * @return DataResponse
0 ignored issues
show
Documentation introduced by
Should the return type not be DataResponse|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
198
	 */
199
	public function receiveFederatedDelivery($apiVersion, $token, $uniqueId, $item) {
200
201
		if ($uniqueId === '' || !$this->configService->isFederatedCirclesAllowed()) {
202
			return $this->federatedFail('federated_not_allowed');
203
		}
204
205
		try {
206
			Circles::compareVersion($apiVersion);
207
			$frame = SharingFrame::fromJSON($item);
208
			$this->sharingFrameService->receiveFrame($token, $uniqueId, $frame);
0 ignored issues
show
Bug introduced by
It seems like $frame defined by \OCA\Circles\Model\SharingFrame::fromJSON($item) on line 207 can be null; however, OCA\Circles\Service\Shar...Service::receiveFrame() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
209
		} catch (SharingFrameAlreadyExistException $e) {
210
			return $this->federatedSuccess();
211
		} catch (Exception $e) {
212
			return $this->federatedFail($e->getMessage());
213
		}
214
215
		$this->miscService->asyncAndLeaveClientOutOfThis('done');
216
		$this->broadcastService->broadcastFrame($frame);
217
		$this->sharingFrameService->forwardSharingFrame($frame);
218
		exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method receiveFederatedDelivery() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
219
	}
220
221
222
	/**
223
	 * updateLink();
224
	 *
225
	 * Update the current status of a link, based on UniqueId and Token.
226
	 *
227
	 * @PublicPage
228
	 * @NoCSRFRequired
229
	 *
230
	 * @param $data
231
	 *
232
	 * @return DataResponse
233
	 */
234
	public function updateLink($data) {
235 View Code Duplication
		if (MiscService::get($data, 'uniqueId') === ''
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
236
			|| !$this->configService->isFederatedCirclesAllowed()) {
237
			return $this->federatedFail('federated_not_allowed');
238
		}
239
240
		try {
241
			Circles::compareVersion(MiscService::get($data, 'apiVersion'));
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...et($data, 'apiVersion') targeting OCA\Circles\Service\MiscService::get() can also be of type string; however, OCA\Circles\Api\v1\Circles::compareVersion() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
242
			$link = $this->federatedLinkService->updateLinkFromRemote(
243
				MiscService::get($data, 'token'), MiscService::get($data, 'uniqueId'),
0 ignored issues
show
Bug introduced by
It seems like \OCA\Circles\Service\Mis...ce::get($data, 'token') targeting OCA\Circles\Service\MiscService::get() can also be of type array; however, OCA\Circles\Service\Fede...:updateLinkFromRemote() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like \OCA\Circles\Service\Mis...:get($data, 'uniqueId') targeting OCA\Circles\Service\MiscService::get() can also be of type array; however, OCA\Circles\Service\Fede...:updateLinkFromRemote() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
244
				MiscService::get($data, 'status')
0 ignored issues
show
Documentation introduced by
\OCA\Circles\Service\Mis...e::get($data, 'status') is of type string|array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
245
			);
246
		} catch (Exception $e) {
247
			return $this->federatedFail($e->getMessage());
248
		}
249
250
		return $this->federatedSuccess(
251
			['status' => 1, 'link' => $link], $link
252
		);
253
	}
254
255
256
	/**
257
	 * send a positive response to a request with an array of data, and confirm
258
	 * the identity of the link with a token
259
	 *
260
	 * @param array $data
261
	 * @param FederatedLink $link
0 ignored issues
show
Documentation introduced by
Should the type for parameter $link not be null|FederatedLink?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
262
	 *
263
	 * @return DataResponse
264
	 */
265
	private function federatedSuccess(array $data = array(), FederatedLink $link = null) {
266
267
		if (!key_exists('status', $data)) {
268
			$data['status'] = 1;
269
		}
270
271
		if ($link !== null) {
272
			$data = array_merge($data, ['token' => $link->getToken(true)]);
273
		}
274
275
		return new DataResponse($data, Http::STATUS_OK);
276
	}
277
278
279
	/**
280
	 * send a negative response to a request, with a reason of the failure.
281
	 *
282
	 * @param string $reason
283
	 *
284
	 * @return DataResponse
285
	 */
286
	private function federatedFail($reason) {
287
		$this->miscService->log(2, 'federated fail: ' . $reason);
288
289
		return new DataResponse(
290
			[
291
				'status' => FederatedLink::STATUS_ERROR,
292
				'reason' => $reason
293
			],
294
			Http::STATUS_OK
295
		);
296
	}
297
}