|
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( |
|
|
|
|
|
|
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; |
|
|
|
|
|
|
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); |
|
|
|
|
|
|
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()); |
|
|
|
|
|
|
230
|
|
|
|
|
231
|
|
|
$response = $client->put($url, FederatedLinkService::generateClientBodyData($link, $args)); |
|
232
|
|
|
$result = $this->federatedLinkService->parseClientRequestResult($response); |
|
|
|
|
|
|
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: |
|
|
|
|
|
|
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
|
|
|
} |
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.