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

parseRequestLinkError()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 11
nc 2
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\Service;
28
29
30
use Exception;
31
use OCA\Circles\Db\CirclesRequest;
32
use OCA\Circles\Db\FederatedLinksRequest;
33
use OCA\Circles\Exceptions\CircleTypeNotValidException;
34
use OCA\Circles\Exceptions\FederatedCircleLinkFormatException;
35
use OCA\Circles\Exceptions\FederatedCircleNotAllowedException;
36
use OCA\Circles\Exceptions\FederatedRemoteDoesNotAllowException;
37
use OCA\Circles\Model\Circle;
38
use OCA\Circles\Model\FederatedLink;
39
use OCP\Http\Client\IClientService;
40
use OCP\IL10N;
41
42
class FederatedLinkCreationService {
43
44
	/** @var string */
45
	private $userId;
46
47
	/** @var IL10N */
48
	private $l10n;
49
50
	/** @var CirclesRequest */
51
	private $circlesRequest;
52
53
	/** @var ConfigService */
54
	private $configService;
55
56
	/** @var CirclesService */
57
	private $circlesService;
58
59
	/** @var BroadcastService */
60
	private $broadcastService;
61
62
	/** @var BroadcastService */
63
	private $federatedLinkService;
64
65
	/** @var FederatedLinksRequest */
66
	private $federatedLinksRequest;
67
68
	/** @var EventsService */
69
	private $eventsService;
70
71
	/** @var IClientService */
72
	private $clientService;
73
74
	/** @var MiscService */
75
	private $miscService;
76
77
78
	/**
79
	 * FederatedLinkCreationService constructor.
80
	 *
81
	 * @param string $UserId
82
	 * @param IL10N $l10n
83
	 * @param CirclesRequest $circlesRequest
84
	 * @param ConfigService $configService
85
	 * @param CirclesService $circlesService
86
	 * @param BroadcastService $broadcastService
87
	 * @param FederatedLinkService $federatedService
88
	 * @param FederatedLinksRequest $federatedLinksRequest
89
	 * @param EventsService $eventsService
90
	 * @param IClientService $clientService
91
	 * @param MiscService $miscService
92
	 */
93 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...
94
		$UserId, IL10N $l10n, CirclesRequest $circlesRequest, ConfigService $configService,
95
		CirclesService $circlesService, BroadcastService $broadcastService,
96
		FederatedLinkService $federatedService,
97
		FederatedLinksRequest $federatedLinksRequest, EventsService $eventsService,
98
		IClientService $clientService, MiscService $miscService
99
	) {
100
		$this->userId = $UserId;
101
		$this->l10n = $l10n;
102
		$this->circlesRequest = $circlesRequest;
103
		$this->configService = $configService;
104
		$this->circlesService = $circlesService;
105
		$this->broadcastService = $broadcastService;
106
		$this->federatedLinkService = $federatedService;
0 ignored issues
show
Documentation Bug introduced by
It seems like $federatedService of type object<OCA\Circles\Service\FederatedLinkService> is incompatible with the declared type object<OCA\Circles\Service\BroadcastService> of property $federatedLinkService.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
107
		$this->federatedLinksRequest = $federatedLinksRequest;
108
		$this->eventsService = $eventsService;
109
110
		$this->clientService = $clientService;
111
		$this->miscService = $miscService;
112
	}
113
114
115
	/**
116
	 * createLinkWithRemoteCircle();
117
	 *
118
	 * link to a circle.
119
	 * Function will check if settings allow Federated links between circles, and the format of
120
	 * the link ($remote). If no exception, a request to the remote circle will be initiated
121
	 * using requestLinkWithRemoteCircle()
122
	 *
123
	 * $remote format: <circle_name>@<remote_host>
124
	 *
125
	 * @param string $circleUniqueId
126
	 * @param string $remote
127
	 *
128
	 * @throws Exception
129
	 * @throws FederatedCircleLinkFormatException
130
	 * @throws CircleTypeNotValidException
131
	 *
132
	 * @return FederatedLink
133
	 */
134
	public function createLinkWithRemoteCircle($circleUniqueId, $remote) {
135
136
		if (!$this->configService->isFederatedCirclesAllowed()) {
137
			throw new FederatedCircleNotAllowedException(
138
				$this->l10n->t("Federated circles are not allowed on this Nextcloud")
139
			);
140
		}
141
142
		if (strpos($remote, '@') === false) {
143
			throw new FederatedCircleLinkFormatException(
144
				$this->l10n->t("Federated link does not have a valid format")
145
			);
146
		}
147
148
		try {
149
			return $this->requestLinkWithRemoteCircle($circleUniqueId, $remote);
150
		} catch (Exception $e) {
151
			throw $e;
152
		}
153
	}
154
155
156
	/**
157
	 * requestLinkWithRemoteCircle()
158
	 *
159
	 * Using CircleId, function will get more infos from the database.
160
	 * Will check if author is at least admin and initiate a FederatedLink, save it
161
	 * in the database and send a request to the remote circle using requestLink()
162
	 * If any issue, entry is removed from the database.
163
	 *
164
	 * @param string $circleUniqueId
165
	 * @param string $remote
166
	 *
167
	 * @return FederatedLink
168
	 * @throws Exception
169
	 */
170
	private function requestLinkWithRemoteCircle($circleUniqueId, $remote) {
171
172
		$link = null;
173
		try {
174
			$circle = $this->circlesService->detailsCircle($circleUniqueId);
175
			$circle->getHigherViewer()
176
				   ->hasToBeAdmin();
177
			$circle->hasToBeFederated();
178
			$circle->cantBePersonal();
179
180
			$link = $this->generateNewLink($circle->getUniqueId(), $remote);
181
			$this->forceRequestNewLink($circle, $link);
182
		} catch (Exception $e) {
183
			$this->federatedLinksRequest->delete($link);
0 ignored issues
show
Bug introduced by
It seems like $link defined by null on line 172 can be null; however, OCA\Circles\Db\FederatedLinksRequest::delete() 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...
184
			throw $e;
185
		}
186
187
		return $link;
188
	}
189
190
191
	/**
192
	 * @param $circleUniqueId
193
	 * @param $remote
194
	 *
195
	 * @return FederatedLink
196
	 */
197
	private function generateNewLink($circleUniqueId, $remote) {
198
199
		$link = new FederatedLink();
200
		list($remoteCircle, $remoteAddress) = explode('@', $remote, 2);
201
202
		$link->setCircleId($circleUniqueId)
203
			 ->setLocalAddress($this->configService->getLocalAddress())
204
			 ->setAddress($remoteAddress)
205
			 ->setRemoteCircleName($remoteCircle)
206
			 ->setStatus(FederatedLink::STATUS_LINK_SETUP)
207
			 ->generateToken();
208
209
		$this->federatedLinksRequest->create($link);
210
211
		return $link;
212
	}
213
214
215
	/**
216
	 * requestLink()
217
	 *
218
	 *
219
	 * @param Circle $circle
220
	 * @param FederatedLink $link
221
	 *
222
	 * @return boolean
223
	 * @throws Exception
224
	 */
225
	private function forceRequestNewLink(Circle $circle, FederatedLink &$link) {
226
		try {
227
			$client = $this->clientService->newClient();
228
			$args = ['sourceName' => $circle->getName()];
229
			$url = $this->federatedLinkService->generateLinkRemoteURL($link->getAddress());
0 ignored issues
show
Bug introduced by
The method generateLinkRemoteURL() does not seem to exist on object<OCA\Circles\Service\BroadcastService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
230
231
			$response = $client->put($url, FederatedLinkService::generateClientBodyData($link, $args));
232
			$result = $this->federatedLinkService->parseClientRequestResult($response);
0 ignored issues
show
Bug introduced by
The method parseClientRequestResult() does not seem to exist on object<OCA\Circles\Service\BroadcastService>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
233
234
			$reason = ((key_exists('reason', $result)) ? $result['reason'] : '');
235
			$this->eventOnRequestLink($circle, $link, $result['status'], $reason);
236
237
			$link->setUniqueId($result['uniqueId']);
238
			$this->federatedLinksRequest->update($link);
239
240
			return true;
241
		} catch (Exception $e) {
242
			throw $e;
243
		}
244
	}
245
246
247
	/**
248
	 * eventOnRequestLink();
249
	 *
250
	 * Called by requestLink() will update status and event
251
	 * Will also manage errors returned by the remote link
252
	 *
253
	 * @param Circle $circle
254
	 * @param FederatedLink $link
255
	 * @param int $status
256
	 * @param string $reason
257
	 *
258
	 * @throws Exception
259
	 */
260
	private function eventOnRequestLink(Circle $circle, FederatedLink &$link, $status, $reason) {
261
262
		switch ($status) {
263
			case FederatedLink::STATUS_LINK_UP:
264
				$link->setStatus(FederatedLink::STATUS_LINK_UP);
265
				$this->eventsService->onLinkUp($circle, $link);
266
				break;
267
268
			case  FederatedLink::STATUS_LINK_REQUESTED:
0 ignored issues
show
Coding Style introduced by
As per coding-style, case should be followed by a single space.

As per the PSR-2 coding standard, there must be a space after the case keyword, instead of the test immediately following it.

switch (true) {
    case!isset($a):  //wrong
        doSomething();
        break;
    case !isset($b):  //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
269
				$link->setStatus(FederatedLink::STATUS_REQUEST_SENT);
270
				$this->eventsService->onLinkRequestSent($circle, $link);
271
				break;
272
273
			default:
274
				$this->parseRequestLinkError($reason);
275
		}
276
	}
277
278
279
	/**
280
	 * parseRequestLinkError();
281
	 *
282
	 * Will parse the error reason returned by requestLink() and throw an Exception
283
	 *
284
	 * @param $reason
285
	 *
286
	 * @throws Exception
287
	 * @throws FederatedRemoteDoesNotAllowException
288
	 */
289
	private function parseRequestLinkError($reason) {
290
291
		$convert = [
292
			'federated_not_allowed' => $this->l10n->t(
293
				'Federated circles are not allowed on the remote Nextcloud'
294
			),
295
			'circle_links_disable'  => $this->l10n->t('Remote circle does not accept federated links'),
296
			'duplicate_unique_id'   => $this->l10n->t('Trying to link a circle to itself'),
297
			'duplicate_link'        => $this->l10n->t('This link exists already'),
298
			'circle_does_not_exist' => $this->l10n->t('The requested remote circle does not exist')
299
		];
300
301
		if (key_exists($reason, $convert)) {
302
			throw new FederatedRemoteDoesNotAllowException($convert[$reason]);
303
		}
304
		throw new Exception($reason);
305
	}
306
307
308
}