Completed
Pull Request — master (#544)
by Maxence
02:26
created

CirclesRemote   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 246
Duplicated Lines 29.27 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 11
dl 72
loc 246
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A configure() 0 7 1
F execute() 31 142 16
A saveRemote() 18 18 2
A updateRemote() 23 23 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php declare(strict_types=1);
2
3
4
/**
5
 * Circles - Bring cloud-users closer together.
6
 *
7
 * This file is licensed under the Affero General Public License version 3 or
8
 * later. See the COPYING file.
9
 *
10
 * @author Maxence Lange <[email protected]>
11
 * @copyright 2017
12
 * @license GNU AGPL version 3 or any later version
13
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License as
16
 * published by the Free Software Foundation, either version 3 of the
17
 * License, or (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
26
 *
27
 */
28
29
30
namespace OCA\Circles\Command;
31
32
use daita\MySmallPhpTools\Exceptions\SignatoryException;
33
use daita\MySmallPhpTools\Exceptions\SignatureException;
34
use daita\MySmallPhpTools\Traits\Nextcloud\nc21\TNC21WellKnown;
35
use daita\MySmallPhpTools\Traits\TStringTools;
36
use Exception;
37
use OC\Core\Command\Base;
38
use OCA\Circles\AppInfo\Application;
39
use OCA\Circles\Exceptions\RemoteNotFoundException;
40
use OCA\Circles\Exceptions\RemoteUidException;
41
use OCA\Circles\Model\AppService;
42
use OCA\Circles\Service\RemoteService;
43
use OCP\IL10N;
44
use Symfony\Component\Console\Input\InputArgument;
45
use Symfony\Component\Console\Input\InputInterface;
46
use Symfony\Component\Console\Input\InputOption;
47
use Symfony\Component\Console\Output\OutputInterface;
48
use Symfony\Component\Console\Question\ConfirmationQuestion;
49
50
51
/**
52
 * Class CirclesRemote
53
 *
54
 * @package OCA\Circles\Command
55
 */
56
class CirclesRemote extends Base {
57
58
59
	use TNC21WellKnown;
60
	use TStringTools;
61
62
63
	/** @var IL10N */
64
	private $l10n;
65
66
	/** @var RemoteService */
67
	private $remoteService;
68
69
70
	/**
71
	 * CirclesList constructor.
72
	 *
73
	 * @param RemoteService $remoteService
74
	 */
75
	public function __construct(RemoteService $remoteService) {
76
		parent::__construct();
77
78
		$this->remoteService = $remoteService;
79
		$this->setup('app', 'circles');
80
	}
81
82
83
	/**
84
	 *
85
	 */
86
	protected function configure() {
87
		parent::configure();
88
		$this->setName('circles:remote')
89
			 ->setDescription('remote features')
90
			 ->addArgument('host', InputArgument::REQUIRED, 'host of the remote instance of Nextcloud')
91
			 ->addOption('all', '', InputOption::VALUE_NONE, 'display all information');
92
	}
93
94
95
	/**
96
	 * @param InputInterface $input
97
	 * @param OutputInterface $output
98
	 *
99
	 * @return int
100
	 * @throws Exception
101
	 */
102
	protected function execute(InputInterface $input, OutputInterface $output): int {
103
		$host = $input->getArgument('host');
104
105
		$webfinger = $this->getWebfinger($host, Application::APP_SUBJECT);
106 View Code Duplication
		if ($input->getOption('all')) {
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...
107
			$output->writeln('- Webfinger on <info>' . $host . '</info>');
108
			$output->writeln(json_encode($webfinger, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
109
			$output->writeln('');
110
		}
111
112 View Code Duplication
		if ($input->getOption('all')) {
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...
113
			$circleLink = $this->extractLink(Application::APP_REL, $webfinger);
114
			$output->writeln('- Information about Circles app on <info>' . $host . '</info>');
115
			$output->writeln(json_encode($circleLink, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
116
			$output->writeln('');
117
		}
118
119
		$output->writeln('- Available services on <info>' . $host . '</info>');
120
		foreach ($webfinger->getLinks() as $link) {
121
			$app = $link->getProperty('name');
122
			$ver = $link->getProperty('version');
123
			if ($app !== '') {
124
				$app .= ' ';
125
			}
126
			if ($ver !== '') {
127
				$ver = 'v' . $ver;
128
			}
129
130
			$output->writeln(' * ' . $link->getRel() . ' ' . $app . $ver);
131
		}
132
		$output->writeln('');
133
134
		$output->writeln('- Resources related to Circles on <info>' . $host . '</info>');
135
		$resource = $this->getResourceData($host, Application::APP_SUBJECT, Application::APP_REL);
136
		$output->writeln(json_encode($resource, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
137
		$output->writeln('');
138
139
140
		$tempUid = $resource->g('uid');
141
		$output->writeln(
142
			'- Confirming UID=' . $tempUid . ' from parsed Signatory at <info>' . $host . '</info>'
143
		);
144
145
		try {
146
			$remoteSignatory = $this->remoteService->retrieveSignatory($resource->g('id'), true, true);
147
			$output->writeln(' * No SignatureException: <info>Identity authed</info>');
148
		} catch (SignatureException $e) {
149
			$output->writeln(
150
				'<error>' . $host . ' cannot auth its identity: ' . $e->getMessage() . '</error>'
151
			);
152
153
			return 0;
154
		}
155
156
		$output->writeln(' * Found <info>' . $remoteSignatory->getUid() . '</info>');
157
		if ($remoteSignatory->getUid(true) !== $tempUid) {
158
			$output->writeln('<error>looks like ' . $host . ' is faking its identity');
159
160
			return 0;
161
		}
162
163
		$output->writeln('');
164
165
		$testUrl = $resource->g('test');
166
		$output->writeln('- Testing signed payload on <info>' . $testUrl . '</info>');
167
168
		try {
169
			$localSignatory = $this->remoteService->getAppSignatory();
170
		} catch (SignatoryException $e) {
171
			$output->writeln(
172
				'<error>Federated Circles not enabled locally. Please run ./occ circles:remote:init</error>'
173
			);
174
175
			return 0;
176
		}
177
178
		$payload = [
179
			'test'  => 42,
180
			'token' => $this->uuid()
181
		];
182
		$signedRequest = $this->remoteService->test($testUrl, $payload);
183
		$output->writeln(' * Payload: ');
184
		$output->writeln(json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
185
		$output->writeln('');
186
187
		$output->writeln(' * Clear Signature: ');
188
		$output->writeln('<comment>' . $signedRequest->getClearSignature() . '</comment>');
189
		$output->writeln('');
190
191
		$output->writeln(' * Signed Signature (base64 encoded): ');
192
		$output->writeln('<comment>' . base64_encode($signedRequest->getSignedSignature()) . '</comment>');
193
		$output->writeln('');
194
195
		$result = $signedRequest->getOutgoingRequest()->getResult();
196
		$code = $result->getStatusCode();
197
		$output->writeln(' * Result: ' . (($code === 200) ? '<info>' . $code . '</info>' : $code));
198
		$output->writeln(
199
			json_encode(json_decode($result->getContent(), true), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
200
		);
201
		$output->writeln('');
202
203 View Code Duplication
		if ($input->getOption('all')) {
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...
204
			$output->writeln('');
205
			$output->writeln('<info>### Complete report ###</info>');
206
			$output->writeln(json_encode($signedRequest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
207
			$output->writeln('');
208
		}
209
210
		if ($remoteSignatory->getUid() !== $localSignatory->getUid()) {
211
			$remoteSignatory->setInstance($host);
212
			try {
213
				$stored = new AppService();
214
				$this->remoteService->confirmValidRemote($remoteSignatory, $stored);
215
				$output->writeln(
216
					'<info>The remote instance ' . $host
217
					. ' is already known with this current identity</info>'
218
				);
219
220 View Code Duplication
				if ($remoteSignatory->getInstance() !== $stored->getInstance()) {
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...
221
					$output->writeln(
222
						'- updating host from ' . $stored->getInstance() . ' to '
223
						. $remoteSignatory->getInstance()
224
					);
225
					$this->remoteService->update($remoteSignatory, RemoteService::UPDATE_INSTANCE);
226
				}
227 View Code Duplication
				if ($remoteSignatory->getId() !== $stored->getId()) {
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...
228
					$output->writeln(
229
						'- updating href/Id from ' . $stored->getId() . ' to '
230
						. $remoteSignatory->getId()
231
					);
232
					$this->remoteService->update($remoteSignatory, RemoteService::UPDATE_HREF);
233
				}
234
235
			} catch (RemoteUidException $e) {
236
				$this->updateRemote($input, $output, $remoteSignatory);
237
			} catch (RemoteNotFoundException $e) {
238
				$this->saveRemote($input, $output, $remoteSignatory);
239
			}
240
		}
241
242
		return 0;
243
	}
244
245
246
	/**
247
	 * @param InputInterface $input
248
	 * @param OutputInterface $output
249
	 * @param AppService $remoteSignatory
250
	 */
251 View Code Duplication
	private function saveRemote(InputInterface $input, OutputInterface $output, AppService $remoteSignatory) {
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...
252
		$output->writeln('');
253
		$helper = $this->getHelper('question');
254
255
		$output->writeln(
256
			'The remote instance <info>' . $remoteSignatory->getInstance() . '</info> looks good.'
257
		);
258
		$question = new ConfirmationQuestion(
259
			'Would you like to allow the sharing of your circles with this remote instance ? (y/N) ',
260
			false,
261
			'/^(y|Y)/i'
262
		);
263
264
		if ($helper->ask($input, $output, $question)) {
265
			$this->remoteService->save($remoteSignatory);
266
			$output->writeln('<info>remote instance saved</info>');
267
		}
268
	}
269
270
271
	/**
272
	 * @param InputInterface $input
273
	 * @param OutputInterface $output
274
	 * @param AppService $remoteSignatory
275
	 */
276 View Code Duplication
	private function updateRemote(InputInterface $input, OutputInterface $output, AppService $remoteSignatory
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...
277
	) {
278
		$output->writeln('');
279
		$helper = $this->getHelper('question');
280
281
		$output->writeln(
282
			'The remote instance <info>' . $remoteSignatory->getInstance()
283
			. '</info> is known but <error>its identity has changed.</error>'
284
		);
285
		$output->writeln(
286
			'<comment>If you are not sure on why identity changed, please say No to the next question and contact the admin of the remote instance</comment>'
287
		);
288
		$question = new ConfirmationQuestion(
289
			'Do you consider this new identity as valid and update the entry in the database? (y/N) ',
290
			false,
291
			'/^(y|Y)/i'
292
		);
293
294
		if ($helper->ask($input, $output, $question)) {
295
			$this->remoteService->update($remoteSignatory);
296
			$output->writeln('remote instance updated');
297
		}
298
	}
299
300
301
}
302
303