Skip to content

Commit 0a21d66

Browse files
committed
authenticated communication
1 parent 2c22a59 commit 0a21d66

14 files changed

+1002
-23
lines changed

appinfo/info.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
<command>OCA\Backup\Command\RemoteAdd</command>
2626
<command>OCA\Backup\Command\RemoteList</command>
2727
<command>OCA\Backup\Command\RemoteRemove</command>
28+
29+
<command>OCA\Backup\Command\RestoringBrowse</command>
30+
2831
<!-- <command>OCA\Backup\Command\Create</command>-->
2932
<!-- <command>OCA\Backup\Command\Listing</command>-->
3033
<!-- <command>OCA\Backup\Command\Details</command>-->

appinfo/routes.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
return [
44
'routes' => [
55
['name' => 'Remote#appService', 'url' => '/', 'verb' => 'GET'],
6-
['name' => 'Remote#test', 'url' => '/test', 'verb' => 'GET']
6+
['name' => 'Remote#test', 'url' => '/test', 'verb' => 'GET'],
7+
8+
['name' => 'Remote#listRestoringPoint', 'url' => '/rp', 'verb' => 'GET'],
9+
['name' => 'Remote#detailsRestoringPoint', 'url' => '/rp/{restoringId}', 'verb' => 'GET'],
10+
['name' => 'Remote#partRestoringPoint', 'url' => '/rp/{restoringId}/part', 'verb' => 'GET'],
11+
['name' => 'Remote#downloadRestoringPoint', 'url' => '/rp/{restoringId}/download', 'verb' => 'GET'],
12+
['name' => 'Remote#createRestoringPoint', 'url' => '/rp', 'verb' => 'PUT'],
13+
['name' => 'Remote#updateRestoringPoint', 'url' => '/rp/{restoringId}', 'verb' => 'PUT'],
14+
['name' => 'Remote#uploadRestoringPoint', 'url' => '/rp/{restoringId}', 'verb' => 'POST']
715
]
816
];

lib/Command/RemoteAdd.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
136136
}
137137

138138
$remoteSignatory->setInstance($address);
139-
echo json_encode($knownInstance) . "\n";
140139
if (!is_null($knownInstance)) {
141140
if ($remoteSignatory->getInstance() !== $knownInstance->getInstance()) {
142141
throw new RemoteInstanceDuplicateException(

lib/Command/RestoringBrowse.php

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
/**
7+
* Nextcloud - Backup
8+
*
9+
* This file is licensed under the Affero General Public License version 3 or
10+
* later. See the COPYING file.
11+
*
12+
* @author Maxence Lange <maxence@artificial-owl.com>
13+
* @copyright 2021, Maxence Lange <maxence@artificial-owl.com>
14+
* @license GNU AGPL version 3 or any later version
15+
*
16+
* This program is free software: you can redistribute it and/or modify
17+
* it under the terms of the GNU Affero General Public License as
18+
* published by the Free Software Foundation, either version 3 of the
19+
* License, or (at your option) any later version.
20+
*
21+
* This program is distributed in the hope that it will be useful,
22+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+
* GNU Affero General Public License for more details.
25+
*
26+
* You should have received a copy of the GNU Affero General Public License
27+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
28+
*
29+
*/
30+
31+
32+
namespace OCA\Backup\Command;
33+
34+
35+
use ArtificialOwl\MySmallPhpTools\Exceptions\InvalidItemException;
36+
use ArtificialOwl\MySmallPhpTools\Model\Request;
37+
use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc23\TNC23Deserialize;
38+
use OC\Core\Command\Base;
39+
use OCA\Backup\Db\RemoteRequest;
40+
use OCA\Backup\Exceptions\RemoteInstanceException;
41+
use OCA\Backup\Exceptions\RemoteInstanceNotFoundException;
42+
use OCA\Backup\Exceptions\RemoteResourceNotFoundException;
43+
use OCA\Backup\Model\RemoteInstance;
44+
use OCA\Backup\Model\RestoringPoint;
45+
use OCA\Backup\Service\RemoteStreamService;
46+
use Symfony\Component\Console\Input\InputInterface;
47+
use Symfony\Component\Console\Output\OutputInterface;
48+
use Symfony\Component\Console\Question\ChoiceQuestion;
49+
50+
51+
/**
52+
* Class RemoteList
53+
*
54+
* @package OCA\Backup\Command
55+
*/
56+
class RestoringBrowse extends Base {
57+
58+
59+
use TNC23Deserialize;
60+
61+
62+
const LOCAL = 'local';
63+
64+
65+
/** @var RemoteRequest */
66+
private $remoteRequest;
67+
68+
/** @var RemoteStreamService */
69+
private $remoteStreamService;
70+
71+
72+
public function __construct(RemoteRequest $remoteRequest, RemoteStreamService $remoteStreamService) {
73+
$this->remoteRequest = $remoteRequest;
74+
$this->remoteStreamService = $remoteStreamService;
75+
76+
parent::__construct();
77+
}
78+
79+
80+
/**
81+
*
82+
*/
83+
protected function configure() {
84+
$this->setName('backup:point:browse')
85+
->setDescription('Browse restoring point');
86+
}
87+
88+
89+
/**
90+
* @param InputInterface $input
91+
* @param OutputInterface $output
92+
*
93+
* @return int
94+
* @throws RemoteInstanceException
95+
* @throws RemoteInstanceNotFoundException
96+
* @throws RemoteResourceNotFoundException
97+
*/
98+
protected function execute(InputInterface $input, OutputInterface $output): int {
99+
$instance = $this->selectInstanceToBrowse($input, $output);
100+
101+
$output->writeln('');
102+
$output->writeln('Browsing available Restoring Point on <info>' . $instance . '</info>');
103+
104+
$rp = $this->getRestoringPoint($instance, ($instance === self::LOCAL));
105+
echo 'RP: ' . json_encode($rp, JSON_PRETTY_PRINT);
106+
107+
return 0;
108+
}
109+
110+
111+
/**
112+
* @param InputInterface $input
113+
* @param OutputInterface $output
114+
*
115+
* @return string
116+
*/
117+
private function selectInstanceToBrowse(InputInterface $input, OutputInterface $output): string {
118+
$remote = array_filter(
119+
array_map(
120+
function (RemoteInstance $remoteInstance): ?string {
121+
if (!$remoteInstance->isOutgoing()) {
122+
return null;
123+
}
124+
125+
return $remoteInstance->getInstance();
126+
}, $this->remoteRequest->getAll()
127+
)
128+
);
129+
130+
$output->writeln('');
131+
$helper = $this->getHelper('question');
132+
$question = new ChoiceQuestion(
133+
'Which location to browse ?',
134+
array_merge([self::LOCAL], $remote),
135+
0
136+
);
137+
$question->setErrorMessage('Instance %s is not known.');
138+
139+
return $helper->ask($input, $output, $question);
140+
}
141+
142+
143+
/**
144+
* @param string $instance
145+
* @param bool $local
146+
*
147+
* @return RestoringPoint[]
148+
* @throws RemoteInstanceNotFoundException
149+
* @throws RemoteInstanceException
150+
* @throws RemoteResourceNotFoundException
151+
* @throws InvalidItemException
152+
*/
153+
private function getRestoringPoint(string $instance, bool $local = false): array {
154+
if ($local) {
155+
return [];
156+
}
157+
158+
$result = $this->remoteStreamService->resultRequestRemoteInstance(
159+
$instance,
160+
RemoteInstance::RP_LIST,
161+
Request::TYPE_GET
162+
);
163+
164+
return $this->deserializeArray($result, RestoringPoint::class);
165+
}
166+
167+
}

lib/Controller/RemoteController.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,22 @@
3232
namespace OCA\Backup\Controller;
3333

3434

35+
use ArtificialOwl\MySmallPhpTools\Exceptions\InvalidOriginException;
3536
use ArtificialOwl\MySmallPhpTools\Exceptions\JsonNotRequestedException;
37+
use ArtificialOwl\MySmallPhpTools\Exceptions\MalformedArrayException;
3638
use ArtificialOwl\MySmallPhpTools\Exceptions\SignatoryException;
39+
use ArtificialOwl\MySmallPhpTools\Exceptions\SignatureException;
40+
use ArtificialOwl\MySmallPhpTools\Model\Nextcloud\nc23\NC23SignedRequest;
3741
use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Controller;
42+
use Exception;
3843
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
44+
use OCA\Backup\Exceptions\RemoteRequestException;
45+
use OCA\Backup\IRemoteRequest;
46+
use OCA\Backup\Model\RemoteInstance;
47+
use OCA\Backup\RemoteRequest\ListRestoringPoint;
3948
use OCA\Backup\Service\RemoteStreamService;
4049
use OCP\AppFramework\Controller;
50+
use OCP\AppFramework\Http;
4151
use OCP\AppFramework\Http\DataResponse;
4252
use OCP\IRequest;
4353

@@ -95,5 +105,117 @@ public function appService(): DataResponse {
95105
return new DataResponse($signatory);
96106
}
97107

108+
109+
/**
110+
* @PublicPage
111+
* @NoCSRFRequired
112+
*
113+
* @return DataResponse
114+
*/
115+
public function listRestoringPoint(): DataResponse {
116+
try {
117+
$request = $this->extractRequest(ListRestoringPoint::class);
118+
} catch (Exception $e) {
119+
return $this->exceptionResponse($e, Http::STATUS_UNAUTHORIZED);
120+
}
121+
122+
try {
123+
$request->execute();
124+
125+
return new DataResponse($request->getOutcomeData());
126+
} catch (Exception $e) {
127+
$this->e($e, ['request' => $request]);
128+
129+
return $this->exceptionResponse($e);
130+
}
131+
}
132+
133+
134+
public function detailsRestoringPoint(int $restoringId): DataResponse {
135+
}
136+
137+
138+
public function partRestoringPoint(int $restoringId): DataResponse {
139+
}
140+
141+
142+
public function downloadRestoringPoint(int $restoringId): DataResponse {
143+
}
144+
145+
public function uploadRestoringPoint(int $restoringId): DataResponse {
146+
}
147+
148+
149+
/**
150+
* @param string $class
151+
*
152+
* @return IRemoteRequest
153+
* @throws RemoteRequestException
154+
* @throws SignatoryException
155+
* @throws InvalidOriginException
156+
* @throws MalformedArrayException
157+
* @throws SignatureException
158+
*/
159+
private function extractRequest(string $class = ''): ?IRemoteRequest {
160+
$signed = $this->remoteStreamService->incomingSignedRequest();
161+
$this->confirmRemoteInstance($signed);
162+
163+
if ($class === '') {
164+
return null;
165+
}
166+
167+
$request = new $class();
168+
if (!($request instanceof IRemoteRequest)) {
169+
throw new RemoteRequestException('invalid class ' . $class);
170+
}
171+
172+
$request->import(json_decode($signed->getBody(), true));
173+
$request->setSignedRequest($signed);
174+
175+
return $request;
176+
}
177+
178+
179+
/**
180+
* @param NC23SignedRequest $signedRequest
181+
*
182+
* @return RemoteInstance
183+
* @throws SignatoryException
184+
*/
185+
private function confirmRemoteInstance(NC23SignedRequest $signedRequest): RemoteInstance {
186+
/** @var RemoteInstance $signatory */
187+
$signatory = $signedRequest->getSignatory();
188+
189+
if (!$signatory instanceof RemoteInstance) {
190+
$this->debug('Signatory is not a known RemoteInstance', ['signedRequest' => $signedRequest]);
191+
throw new SignatoryException('Could not confirm identity');
192+
}
193+
194+
if (!$signatory->isIncoming()) {
195+
throw new SignatoryException('Remote instance is not configured as Incoming');
196+
}
197+
198+
return $signatory;
199+
}
200+
201+
202+
/**
203+
* @param Exception $e
204+
* @param int $httpErrorCode
205+
*
206+
* @return DataResponse
207+
*/
208+
public function exceptionResponse(
209+
Exception $e,
210+
int $httpErrorCode = Http::STATUS_BAD_REQUEST
211+
): DataResponse {
212+
return new DataResponse(
213+
[
214+
'message' => $e->getMessage(),
215+
'code' => $e->getCode()
216+
],
217+
($e->getCode() > 0) ? $e->getCode() : $httpErrorCode
218+
);
219+
}
98220
}
99221

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
6+
/**
7+
* Nextcloud - Backup
8+
*
9+
* This file is licensed under the Affero General Public License version 3 or
10+
* later. See the COPYING file.
11+
*
12+
* @author Maxence Lange <maxence@artificial-owl.com>
13+
* @copyright 2021, Maxence Lange <maxence@artificial-owl.com>
14+
* @license GNU AGPL version 3 or any later version
15+
*
16+
* This program is free software: you can redistribute it and/or modify
17+
* it under the terms of the GNU Affero General Public License as
18+
* published by the Free Software Foundation, either version 3 of the
19+
* License, or (at your option) any later version.
20+
*
21+
* This program is distributed in the hope that it will be useful,
22+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+
* GNU Affero General Public License for more details.
25+
*
26+
* You should have received a copy of the GNU Affero General Public License
27+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
28+
*
29+
*/
30+
31+
32+
namespace OCA\Backup\Exceptions;
33+
34+
35+
use Exception;
36+
37+
38+
/**
39+
* Class RemoteInstanceException
40+
*
41+
* @package OCA\Backup\Exceptions
42+
*/
43+
class RemoteInstanceException extends Exception {
44+
45+
}
46+

0 commit comments

Comments
 (0)