Passed
Push — master ( a54c4b...17bc35 )
by Joas
11:21 queued 10s
created

StoragesController::formatStorageForUI()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 15
rs 9.6111
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Jesús Macias <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Juan Pablo Villafáñez <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Robin McCorkell <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Vincent Petry <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
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, version 3,
25
 * along with this program. If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OCA\Files_External\Controller;
30
31
32
use OCA\Files_External\Lib\Auth\AuthMechanism;
33
use OCA\Files_External\Lib\Backend\Backend;
34
use OCA\Files_External\Lib\DefinitionParameter;
35
use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
36
use OCA\Files_External\Lib\StorageConfig;
37
use OCA\Files_External\NotFoundException;
38
use OCA\Files_External\Service\StoragesService;
39
use OCP\AppFramework\Controller;
40
use OCP\AppFramework\Http;
41
use OCP\AppFramework\Http\DataResponse;
42
use OCP\Files\StorageNotAvailableException;
43
use OCP\IL10N;
44
use OCP\ILogger;
45
use OCP\IRequest;
46
47
/**
48
 * Base class for storages controllers
49
 */
50
abstract class StoragesController extends Controller {
51
52
	/**
53
	 * L10N service
54
	 *
55
	 * @var IL10N
56
	 */
57
	protected $l10n;
58
59
	/**
60
	 * Storages service
61
	 *
62
	 * @var StoragesService
63
	 */
64
	protected $service;
65
66
	/**
67
	 * @var ILogger
68
	 */
69
	protected $logger;
70
71
	/**
72
	 * Creates a new storages controller.
73
	 *
74
	 * @param string $AppName application name
75
	 * @param IRequest $request request object
76
	 * @param IL10N $l10n l10n service
77
	 * @param StoragesService $storagesService storage service
78
	 * @param ILogger $logger
79
	 */
80
	public function __construct(
81
		$AppName,
82
		IRequest $request,
83
		IL10N $l10n,
84
		StoragesService $storagesService,
85
		ILogger $logger
86
	) {
87
		parent::__construct($AppName, $request);
88
		$this->l10n = $l10n;
89
		$this->service = $storagesService;
90
		$this->logger = $logger;
91
	}
92
93
	/**
94
	 * Create a storage from its parameters
95
	 *
96
	 * @param string $mountPoint storage mount point
97
	 * @param string $backend backend identifier
98
	 * @param string $authMechanism authentication mechanism identifier
99
	 * @param array $backendOptions backend-specific options
100
	 * @param array|null $mountOptions mount-specific options
101
	 * @param array|null $applicableUsers users for which to mount the storage
102
	 * @param array|null $applicableGroups groups for which to mount the storage
103
	 * @param int|null $priority priority
104
	 *
105
	 * @return StorageConfig|DataResponse
106
	 */
107
	protected function createStorage(
108
		$mountPoint,
109
		$backend,
110
		$authMechanism,
111
		$backendOptions,
112
		$mountOptions = null,
113
		$applicableUsers = null,
114
		$applicableGroups = null,
115
		$priority = null
116
	) {
117
		try {
118
			return $this->service->createStorage(
119
				$mountPoint,
120
				$backend,
121
				$authMechanism,
122
				$backendOptions,
123
				$mountOptions,
124
				$applicableUsers,
125
				$applicableGroups,
126
				$priority
127
			);
128
		} catch (\InvalidArgumentException $e) {
129
			$this->logger->logException($e);
130
			return new DataResponse(
131
				[
132
					'message' => (string)$this->l10n->t('Invalid backend or authentication mechanism class')
133
				],
134
				Http::STATUS_UNPROCESSABLE_ENTITY
135
			);
136
		}
137
	}
138
139
	/**
140
	 * Validate storage config
141
	 *
142
	 * @param StorageConfig $storage storage config
143
	 *1
144
	 * @return DataResponse|null returns response in case of validation error
145
	 */
146
	protected function validate(StorageConfig $storage) {
147
		$mountPoint = $storage->getMountPoint();
148
		if ($mountPoint === '') {
149
			return new DataResponse(
150
				[
151
					'message' => (string)$this->l10n->t('Invalid mount point'),
152
				],
153
				Http::STATUS_UNPROCESSABLE_ENTITY
154
			);
155
		}
156
157
		if ($storage->getBackendOption('objectstore')) {
158
			// objectstore must not be sent from client side
159
			return new DataResponse(
160
				[
161
					'message' => (string)$this->l10n->t('Objectstore forbidden'),
162
				],
163
				Http::STATUS_UNPROCESSABLE_ENTITY
164
			);
165
		}
166
167
		/** @var Backend */
168
		$backend = $storage->getBackend();
169
		/** @var AuthMechanism */
170
		$authMechanism = $storage->getAuthMechanism();
171
		if ($backend->checkDependencies()) {
172
			// invalid backend
173
			return new DataResponse(
174
				[
175
					'message' => (string)$this->l10n->t('Invalid storage backend "%s"', [
176
						$backend->getIdentifier(),
177
					]),
178
				],
179
				Http::STATUS_UNPROCESSABLE_ENTITY
180
			);
181
		}
182
183
		if (!$backend->isVisibleFor($this->service->getVisibilityType())) {
0 ignored issues
show
Bug introduced by
$this->service->getVisibilityType() of type string is incompatible with the type integer expected by parameter $visibility of OCA\Files_External\Lib\B...Backend::isVisibleFor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

183
		if (!$backend->isVisibleFor(/** @scrutinizer ignore-type */ $this->service->getVisibilityType())) {
Loading history...
184
			// not permitted to use backend
185
			return new DataResponse(
186
				[
187
					'message' => (string)$this->l10n->t('Not permitted to use backend "%s"', [
188
						$backend->getIdentifier(),
189
					]),
190
				],
191
				Http::STATUS_UNPROCESSABLE_ENTITY
192
			);
193
		}
194
		if (!$authMechanism->isVisibleFor($this->service->getVisibilityType())) {
0 ignored issues
show
Bug introduced by
$this->service->getVisibilityType() of type string is incompatible with the type integer expected by parameter $visibility of OCA\Files_External\Lib\A...chanism::isVisibleFor(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

194
		if (!$authMechanism->isVisibleFor(/** @scrutinizer ignore-type */ $this->service->getVisibilityType())) {
Loading history...
195
			// not permitted to use auth mechanism
196
			return new DataResponse(
197
				[
198
					'message' => (string)$this->l10n->t('Not permitted to use authentication mechanism "%s"', [
199
						$authMechanism->getIdentifier(),
200
					]),
201
				],
202
				Http::STATUS_UNPROCESSABLE_ENTITY
203
			);
204
		}
205
206
		if (!$backend->validateStorage($storage)) {
207
			// unsatisfied parameters
208
			return new DataResponse(
209
				[
210
					'message' => (string)$this->l10n->t('Unsatisfied backend parameters'),
211
				],
212
				Http::STATUS_UNPROCESSABLE_ENTITY
213
			);
214
		}
215
		if (!$authMechanism->validateStorage($storage)) {
216
			// unsatisfied parameters
217
			return new DataResponse(
218
				[
219
					'message' => (string)$this->l10n->t('Unsatisfied authentication mechanism parameters'),
220
				],
221
				Http::STATUS_UNPROCESSABLE_ENTITY
222
			);
223
		}
224
225
		return null;
226
	}
227
228
	protected function manipulateStorageConfig(StorageConfig $storage) {
229
		/** @var AuthMechanism */
230
		$authMechanism = $storage->getAuthMechanism();
231
		$authMechanism->manipulateStorageConfig($storage);
232
		/** @var Backend */
233
		$backend = $storage->getBackend();
234
		$backend->manipulateStorageConfig($storage);
235
	}
236
237
	/**
238
	 * Check whether the given storage is available / valid.
239
	 *
240
	 * Note that this operation can be time consuming depending
241
	 * on whether the remote storage is available or not.
242
	 *
243
	 * @param StorageConfig $storage storage configuration
244
	 * @param bool $testOnly whether to storage should only test the connection or do more things
245
	 */
246
	protected function updateStorageStatus(StorageConfig &$storage, $testOnly = true) {
247
		try {
248
			$this->manipulateStorageConfig($storage);
249
250
			/** @var Backend */
251
			$backend = $storage->getBackend();
252
			// update status (can be time-consuming)
253
			$storage->setStatus(
254
				\OC_Mount_Config::getBackendStatus(
255
					$backend->getStorageClass(),
256
					$storage->getBackendOptions(),
257
					false,
258
					$testOnly
259
				)
260
			);
261
		} catch (InsufficientDataForMeaningfulAnswerException $e) {
262
			$status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE;
263
			$storage->setStatus(
264
				$status,
265
				$this->l10n->t('Insufficient data: %s', [$e->getMessage()])
266
			);
267
		} catch (StorageNotAvailableException $e) {
268
			$storage->setStatus(
269
				$e->getCode(),
270
				$this->l10n->t('%s', [$e->getMessage()])
271
			);
272
		} catch (\Exception $e) {
273
			// FIXME: convert storage exceptions to StorageNotAvailableException
274
			$storage->setStatus(
275
				StorageNotAvailableException::STATUS_ERROR,
276
				get_class($e) . ': ' . $e->getMessage()
277
			);
278
		}
279
	}
280
281
	/**
282
	 * Get all storage entries
283
	 *
284
	 * @return DataResponse
285
	 */
286
	public function index() {
287
		$storages = $this->formatStoragesForUI($this->service->getStorages());
288
289
		return new DataResponse(
290
			$storages,
291
			Http::STATUS_OK
292
		);
293
	}
294
295
	protected function formatStoragesForUI(array $storages): array {
296
		return array_map(function ($storage) {
297
			return $this->formatStorageForUI($storage);
298
		}, $storages);
299
	}
300
301
	protected function formatStorageForUI(StorageConfig $storage): StorageConfig {
302
		/** @var DefinitionParameter[] $parameters */
303
		$parameters = array_merge($storage->getBackend()->getParameters(), $storage->getAuthMechanism()->getParameters());
304
305
		$options = $storage->getBackendOptions();
306
		foreach ($options as $key => $value) {
307
			foreach ($parameters as $parameter) {
308
				if ($parameter->getName() === $key && $parameter->getType() === DefinitionParameter::VALUE_PASSWORD) {
309
					$storage->setBackendOption($key, DefinitionParameter::UNMODIFIED_PLACEHOLDER);
310
					break;
311
				}
312
			}
313
		}
314
315
		return $storage;
316
	}
317
318
	/**
319
	 * Get an external storage entry.
320
	 *
321
	 * @param int $id storage id
322
	 * @param bool $testOnly whether to storage should only test the connection or do more things
323
	 *
324
	 * @return DataResponse
325
	 */
326
	public function show($id, $testOnly = true) {
327
		try {
328
			$storage = $this->service->getStorage($id);
329
330
			$this->updateStorageStatus($storage, $testOnly);
331
		} catch (NotFoundException $e) {
332
			return new DataResponse(
333
				[
334
					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id]),
335
				],
336
				Http::STATUS_NOT_FOUND
337
			);
338
		}
339
340
		return new DataResponse(
341
			$this->formatStorageForUI($storage),
342
			Http::STATUS_OK
343
		);
344
	}
345
346
	/**
347
	 * Deletes the storage with the given id.
348
	 *
349
	 * @param int $id storage id
350
	 *
351
	 * @return DataResponse
352
	 */
353
	public function destroy($id) {
354
		try {
355
			$this->service->removeStorage($id);
356
		} catch (NotFoundException $e) {
357
			return new DataResponse(
358
				[
359
					'message' => (string)$this->l10n->t('Storage with ID "%d" not found', [$id]),
360
				],
361
				Http::STATUS_NOT_FOUND
362
			);
363
		}
364
365
		return new DataResponse([], Http::STATUS_NO_CONTENT);
366
	}
367
368
}
369