Completed
Push — master ( 4a5e09...ab41ba )
by Maxence
02:19
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\IRequest;
47
48
class FederatedController extends Controller {
49
50
	/** @var ConfigService */
51
	protected $configService;
52
53
	/** @var CirclesService */
54
	protected $circlesService;
55
56
	/** @var MembersService */
57
	protected $membersService;
58
59
	/** @var SharingFrameService */
60
	protected $sharingFrameService;
61
62
	/** @var BroadcastService */
63
	protected $broadcastService;
64
65
	/** @var FederatedLinkService */
66
	protected $federatedLinkService;
67
68
	/** @var FederatedLinkCreationService */
69
	protected $federatedLinkCreationService;
70
71
	/** @var MiscService */
72
	protected $miscService;
73
74
75
	/**
76
	 * BaseController constructor.
77
	 *
78
	 * @param string $appName
79
	 * @param IRequest $request
80
	 * @param ConfigService $configService
81
	 * @param CirclesService $circlesService
82
	 * @param SharingFrameService $sharingFrameService
83
	 * @param BroadcastService $broadcastService
84
	 * @param FederatedLinkService $federatedLinkService
85
	 * @param FederatedLinkCreationService $federatedLinkCreationService
86
	 * @param MiscService $miscService
87
	 */
88 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...
89
		$appName, IRequest $request, ConfigService $configService, CirclesService $circlesService,
90
		SharingFrameService $sharingFrameService, BroadcastService $broadcastService,
91
		FederatedLinkService $federatedLinkService,
92
		FederatedLinkCreationService $federatedLinkCreationService, MiscService $miscService
93
	) {
94
		parent::__construct($appName, $request);
95
		$this->configService = $configService;
96
		$this->circlesService = $circlesService;
97
		$this->sharingFrameService = $sharingFrameService;
98
		$this->broadcastService = $broadcastService;
99
		$this->federatedLinkService = $federatedLinkService;
100
		$this->federatedLinkCreationService = $federatedLinkCreationService;
101
		$this->miscService = $miscService;
102
	}
103
104
105
	/**
106
	 * requestedLink()
107
	 *
108
	 * Called when a remote circle want to create a link.
109
	 * The function check if it is possible first; then create a link-object
110
	 * and sent it to be saved in the database.
111
	 *
112
	 * @PublicPage
113
	 * @NoCSRFRequired
114
	 *
115
	 * @param $data
116
	 *
117
	 * @return DataResponse
118
	 */
119
	public function requestedLink($data) {
120 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...
121
			|| !$this->configService->isFederatedCirclesAllowed()) {
122
			return $this->federatedFail('federated_not_allowed');
123
		}
124
125
		try {
126
			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...
127
			$circle = $this->circlesService->infoCircleByName(MiscService::get($data, 'linkTo'));
128
			$link = $this->generateNewLink($data);
129
130
			$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 127 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...
131
132
			return $this->federatedSuccess(
133
				['status' => $link->getStatus(), 'uniqueId' => $circle->getUniqueId(true)], $link
134
			);
135
		} catch (CircleDoesNotExistException $e) {
136
			return $this->federatedFail('circle_does_not_exist');
137
		} catch (Exception $e) {
138
			return $this->federatedFail($e->getMessage());
139
		}
140
	}
141
142
143
	/**
144
	 * @param $data
145
	 *
146
	 * @return FederatedLink
147
	 */
148
	private function generateNewLink($data) {
149
		MiscService::mustContains($data, ['token', 'uniqueId', 'sourceName', 'address']);
150
		$link = new FederatedLink();
151
152
		$link->setToken(MiscService::get($data, 'token'))
153
			 ->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...
154
			 ->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...
155
			 ->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...
156
157
		return $link;
158
	}
159
160
	/**
161
	 * receiveFederatedDelivery()
162
	 *
163
	 * Note: this function will close the request mid-run from the client but will still
164
	 * running its process.
165
	 *
166
	 * Called by a remote circle to broadcast a Share item, the function will save the item
167
	 * in the database and broadcast it locally. A status response is sent to the remote to free
168
	 * the remote process before starting to broadcast the item to other federated links.
169
	 *
170
	 * @PublicPage
171
	 * @NoCSRFRequired
172
	 *
173
	 * @param array $apiVersion
174
	 * @param string $token
175
	 * @param string $uniqueId
176
	 * @param string $item
177
	 *
178
	 * @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...
179
	 */
180
	public function receiveFederatedDelivery($apiVersion, $token, $uniqueId, $item) {
181
182
		if ($uniqueId === '' || !$this->configService->isFederatedCirclesAllowed()) {
183
			return $this->federatedFail('federated_not_allowed');
184
		}
185
186
		try {
187
			Circles::compareVersion($apiVersion);
188
			$frame = SharingFrame::fromJSON($item);
189
			$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 188 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...
190
		} catch (SharingFrameAlreadyExistException $e) {
191
			return $this->federatedSuccess();
192
		} catch (Exception $e) {
193
			return $this->federatedFail($e->getMessage());
194
		}
195
196
		$this->miscService->asyncAndLeaveClientOutOfThis('done');
197
		$this->broadcastService->broadcastFrame($frame);
198
		$this->sharingFrameService->forwardSharingFrame($frame);
199
		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...
200
	}
201
202
203
	/**
204
	 * updateLink();
205
	 *
206
	 * Update the current status of a link, based on UniqueId and Token.
207
	 *
208
	 * @PublicPage
209
	 * @NoCSRFRequired
210
	 *
211
	 * @param $data
212
	 *
213
	 * @return DataResponse
214
	 */
215
	public function updateLink($data) {
216 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...
217
			|| !$this->configService->isFederatedCirclesAllowed()) {
218
			return $this->federatedFail('federated_not_allowed');
219
		}
220
221
		try {
222
			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...
223
			$link = $this->federatedLinkService->updateLinkFromRemote(
224
				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...
225
				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...
226
			);
227
		} catch (Exception $e) {
228
			return $this->federatedFail($e->getMessage());
229
		}
230
231
		return $this->federatedSuccess(
232
			['status' => 1, 'link' => $link], $link
233
		);
234
	}
235
236
237
	/**
238
	 * send a positive response to a request with an array of data, and confirm
239
	 * the identity of the link with a token
240
	 *
241
	 * @param array $data
242
	 * @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...
243
	 *
244
	 * @return DataResponse
245
	 */
246
	private function federatedSuccess(array $data = array(), FederatedLink $link = null) {
247
248
		if (!key_exists('status', $data)) {
249
			$data['status'] = 1;
250
		}
251
252
		if ($link !== null) {
253
			$data = array_merge($data, ['token' => $link->getToken(true)]);
254
		}
255
256
		return new DataResponse($data, Http::STATUS_OK);
257
	}
258
259
260
	/**
261
	 * send a negative response to a request, with a reason of the failure.
262
	 *
263
	 * @param string $reason
264
	 *
265
	 * @return DataResponse
266
	 */
267
	private function federatedFail($reason) {
268
		$this->miscService->log(2, 'federated fail: ' . $reason);
269
270
		return new DataResponse(
271
			[
272
				'status' => FederatedLink::STATUS_ERROR,
273
				'reason' => $reason
274
			],
275
			Http::STATUS_OK
276
		);
277
	}
278
}