Completed
Push — master ( d4ce93...77c8bc )
by Maxence
02:50
created

FederatedController::generateNewLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 1
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\FederatedLinkCreationException;
34
use OCA\Circles\Exceptions\SharingFrameAlreadyExistException;
35
use OCA\Circles\Model\FederatedLink;
36
use OCA\Circles\Model\SharingFrame;
37
use OCA\Circles\Service\CirclesService;
38
use OCA\Circles\Service\ConfigService;
39
use OCA\Circles\Service\FederatedLinkService;
40
use OCA\Circles\Service\MembersService;
41
use OCA\Circles\Service\MiscService;
42
use OCA\Circles\Service\SharingFrameService;
43
use OCP\AppFramework\Http\DataResponse;
44
use OCP\IL10N;
45
use Punic\Misc;
46
47
class FederatedController extends BaseController {
48
49
	/** @var string */
50
	protected $userId;
51
52
	/** @var IL10N */
53
	protected $l10n;
54
55
	/** @var ConfigService */
56
	protected $configService;
57
58
	/** @var CirclesService */
59
	protected $circlesService;
60
61
	/** @var MembersService */
62
	protected $membersService;
63
64
	/** @var SharingFrameService */
65
	protected $sharingFrameService;
66
67
	/** @var FederatedLinkService */
68
	protected $federatedLinkService;
69
70
	/** @var MiscService */
71
	protected $miscService;
72
73
74
	/**
75
	 * requestedLink()
76
	 *
77
	 * Called when a remote circle want to create a link.
78
	 * The function check if it is possible first; then create a link-object
79
	 * and sent it to be saved in the database.
80
	 *
81
	 * @PublicPage
82
	 * @NoCSRFRequired
83
	 *
84
	 * @param $data
85
	 *
86
	 * @return DataResponse
87
	 */
88
	public function requestedLink($data) {
89 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...
90
			|| !$this->configService->isFederatedCirclesAllowed()) {
91
			return $this->federatedFail('federated_not_allowed');
92
		}
93
94
		try {
95
			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...
96
			$circle = $this->circlesService->infoCircleByName(MiscService::get($data, 'linkTo'));
97
			$link = $this->generateNewLink($data);
98
99
			$this->federatedLinkService->initiateLink($circle, $link);
0 ignored issues
show
Bug introduced by
It seems like $circle defined by $this->circlesService->i...::get($data, 'linkTo')) on line 96 can be null; however, OCA\Circles\Service\Fede...Service::initiateLink() 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...
100
101
			return $this->federatedSuccess(
102
				['status' => $link->getStatus(), 'uniqueId' => $circle->getUniqueId(true)], $link
103
			);
104
		} catch (CircleDoesNotExistException $e) {
105
			return $this->federatedFail('circle_does_not_exist');
106
		} catch (Exception $e) {
107
			return $this->federatedFail($e->getMessage());
108
		}
109
	}
110
111
112
	/**
113
	 * @param $data
114
	 *
115
	 * @return FederatedLink
116
	 */
117
	private function generateNewLink($data) {
118
		MiscService::mustContains($data, ['token', 'uniqueId', 'sourceName', 'address']);
119
		$link = new FederatedLink();
120
121
		$link->setToken(MiscService::get($data, 'token'))
122
			 ->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...
123
			 ->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...
124
			 ->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...
125
126
		return $link;
127
	}
128
129
	/**
130
	 * receiveFederatedDelivery()
131
	 *
132
	 * Note: this function will close the request mid-run from the client but will still
133
	 * running its process.
134
	 *
135
	 * Called by a remote circle to broadcast a Share item, the function will save the item
136
	 * in the database and broadcast it locally. A status response is sent to the remote to free
137
	 * the remote process before starting to broadcast the item to other federated links.
138
	 *
139
	 * @PublicPage
140
	 * @NoCSRFRequired
141
	 *
142
	 * @param array $apiVersion
143
	 * @param string $token
144
	 * @param string $uniqueId
145
	 * @param string $item
146
	 *
147
	 * @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...
148
	 */
149
	public function receiveFederatedDelivery($apiVersion, $token, $uniqueId, $item) {
150
151
		if ($uniqueId === '' || !$this->configService->isFederatedCirclesAllowed()) {
152
			return $this->federatedFail('federated_not_allowed');
153
		}
154
155
		try {
156
			Circles::compareVersion($apiVersion);
157
			$frame = SharingFrame::fromJSON($item);
158
			$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 157 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...
159
		} catch (SharingFrameAlreadyExistException $e) {
160
			return $this->federatedSuccess();
161
		} catch (Exception $e) {
162
			return $this->federatedFail($e->getMessage());
163
		}
164
165
		$this->miscService->asyncAndLeaveClientOutOfThis('done');
166
		$this->broadcastService->broadcastFrame($frame);
167
		$this->sharingFrameService->forwardSharingFrame($frame);
168
		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...
169
	}
170
171
172
	/**
173
	 * updateLink();
174
	 *
175
	 * Update the current status of a link, based on UniqueId and Token.
176
	 *
177
	 * @PublicPage
178
	 * @NoCSRFRequired
179
	 *
180
	 * @param $data
181
	 *
182
	 * @return DataResponse
183
	 */
184
	public function updateLink($data) {
185 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...
186
			|| !$this->configService->isFederatedCirclesAllowed()) {
187
			return $this->federatedFail('federated_not_allowed');
188
		}
189
190
		try {
191
			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...
192
			$link = $this->federatedLinkService->updateLinkFromRemote(
193
				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...
194
				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...
195
			);
196
		} catch (Exception $e) {
197
			return $this->federatedFail($e->getMessage());
198
		}
199
200
		return $this->federatedSuccess(
201
			['status' => 1, 'link' => $link], $link
202
		);
203
	}
204
205
206
	/**
207
	 * send a positive response to a request with an array of data, and confirm
208
	 * the identity of the link with a token
209
	 *
210
	 * @param array $data
211
	 * @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...
212
	 *
213
	 * @return DataResponse
214
	 */
215
	private function federatedSuccess(array $data = array(), FederatedLink $link = null) {
216
217
		if (!key_exists('status', $data)) {
218
			$data['status'] = 1;
219
		}
220
221
		if ($link !== null) {
222
			$data = array_merge($data, ['token' => $link->getToken(true)]);
223
		}
224
225
		return new DataResponse($data, Http::STATUS_OK);
226
	}
227
228
229
	/**
230
	 * send a negative response to a request, with a reason of the failure.
231
	 *
232
	 * @param string $reason
233
	 *
234
	 * @return DataResponse
235
	 */
236
	private function federatedFail($reason) {
237
		$this->miscService->log(2, 'federated fail: ' . $reason);
238
239
		return new DataResponse(
240
			[
241
				'status' => FederatedLink::STATUS_ERROR,
242
				'reason' => $reason
243
			],
244
			Http::STATUS_OK
245
		);
246
	}
247
}