Completed
Push — master ( 41fdc1...6665fc )
by Maxence
02:50
created

FederatedController::federatedFail()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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