Completed
Pull Request — master (#26700)
by Philipp
08:19
created

files_external/lib/Service/DBConfigService.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @author Joas Schilling <[email protected]>
4
 * @author Robin Appelman <[email protected]>
5
 * @author Robin McCorkell <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud GmbH.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\Files_External\Service;
25
26
use OCP\DB\QueryBuilder\IQueryBuilder;
27
use OCP\IDBConnection;
28
use OCP\Security\ICrypto;
29
30
/**
31
 * Stores the mount config in the database
32
 */
33
class DBConfigService {
34
	const MOUNT_TYPE_ADMIN = 1;
35
	const MOUNT_TYPE_PERSONAl = 2;
36
37
	const APPLICABLE_TYPE_GLOBAL = 1;
38
	const APPLICABLE_TYPE_GROUP = 2;
39
	const APPLICABLE_TYPE_USER = 3;
40
41
	/**
42
	 * @var IDBConnection
43
	 */
44
	private $connection;
45
46
	/**
47
	 * @var ICrypto
48
	 */
49
	private $crypto;
50
51
	/**
52
	 * DBConfigService constructor.
53
	 *
54
	 * @param IDBConnection $connection
55
	 * @param ICrypto $crypto
56
	 */
57
	public function __construct(IDBConnection $connection, ICrypto $crypto) {
58
		$this->connection = $connection;
59
		$this->crypto = $crypto;
60
	}
61
62
	/**
63
	 * @param int $mountId
64
	 * @return array
65
	 */
66
	public function getMountById($mountId) {
67
		$builder = $this->connection->getQueryBuilder();
68
		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
69
			->from('external_mounts', 'm')
70
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
71
		$mounts = $this->getMountsFromQuery($query);
72
		if (count($mounts) > 0) {
73
			return $mounts[0];
74
		} else {
75
			return null;
76
		}
77
	}
78
79
	/**
80
	 * Get all configured mounts
81
	 *
82
	 * @return array
83
	 */
84
	public function getAllMounts() {
85
		$builder = $this->connection->getQueryBuilder();
86
		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
87
			->from('external_mounts');
88
		return $this->getMountsFromQuery($query);
89
	}
90
91
	/**
92
	 * Get admin defined mounts
93
	 *
94
	 * @return array
95
	 */
96
	public function getAdminMounts() {
97
		$builder = $this->connection->getQueryBuilder();
98
		$query = $builder->select(['mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'type'])
99
			->from('external_mounts')
100
			->where($builder->expr()->eq('type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
101
		return $this->getMountsFromQuery($query);
102
	}
103
104
	protected function getForQuery(IQueryBuilder $builder, $type, $value) {
105
		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
106
			->from('external_mounts', 'm')
107
			->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
108
			->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
109
110 View Code Duplication
		if (is_null($value)) {
0 ignored issues
show
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...
111
			$query = $query->andWhere($builder->expr()->isNull('a.value'));
112
		} else {
113
			$query = $query->andWhere($builder->expr()->eq('a.value', $builder->createNamedParameter($value)));
114
		}
115
116
		return $query;
117
	}
118
119
	/**
120
	 * Get mounts by applicable
121
	 *
122
	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
123
	 * @param string|null $value user_id, group_id or null for global mounts
124
	 * @return array
125
	 */
126
	public function getMountsFor($type, $value) {
127
		$builder = $this->connection->getQueryBuilder();
128
		$query = $this->getForQuery($builder, $type, $value);
129
130
		return $this->getMountsFromQuery($query);
131
	}
132
133
	/**
134
	 * Get admin defined mounts by applicable
135
	 *
136
	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
137
	 * @param string|null $value user_id, group_id or null for global mounts
138
	 * @return array
139
	 */
140 View Code Duplication
	public function getAdminMountsFor($type, $value) {
141
		$builder = $this->connection->getQueryBuilder();
142
		$query = $this->getForQuery($builder, $type, $value);
143
		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
144
145
		return $this->getMountsFromQuery($query);
146
	}
147
148
	/**
149
	 * Get admin defined mounts for multiple applicable
150
	 *
151
	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
152
	 * @param string[] $values user_ids or group_ids
153
	 * @return array
154
	 */
155
	public function getAdminMountsForMultiple($type, array $values) {
156
		$builder = $this->connection->getQueryBuilder();
157
		$params = array_map(function ($value) use ($builder) {
158
			return $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR);
159
		}, $values);
160
161
		$query = $builder->select(['m.mount_id', 'mount_point', 'storage_backend', 'auth_backend', 'priority', 'm.type'])
162
			->from('external_mounts', 'm')
163
			->innerJoin('m', 'external_applicable', 'a', $builder->expr()->eq('m.mount_id', 'a.mount_id'))
164
			->where($builder->expr()->eq('a.type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
165
			->andWhere($builder->expr()->in('a.value', $params));
166
		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_ADMIN, IQueryBuilder::PARAM_INT)));
167
168
		return $this->getMountsFromQuery($query);
169
	}
170
171
	/**
172
	 * Get user defined mounts by applicable
173
	 *
174
	 * @param int $type any of the self::APPLICABLE_TYPE_ constants
175
	 * @param string|null $value user_id, group_id or null for global mounts
176
	 * @return array
177
	 */
178 View Code Duplication
	public function getUserMountsFor($type, $value) {
179
		$builder = $this->connection->getQueryBuilder();
180
		$query = $this->getForQuery($builder, $type, $value);
181
		$query->andWhere($builder->expr()->eq('m.type', $builder->expr()->literal(self::MOUNT_TYPE_PERSONAl, IQueryBuilder::PARAM_INT)));
182
183
		return $this->getMountsFromQuery($query);
184
	}
185
186
	/**
187
	 * Add a mount to the database
188
	 *
189
	 * @param string $mountPoint
190
	 * @param string $storageBackend
191
	 * @param string $authBackend
192
	 * @param int $priority
193
	 * @param int $type self::MOUNT_TYPE_ADMIN or self::MOUNT_TYPE_PERSONAL
194
	 * @return int the id of the new mount
195
	 */
196
	public function addMount($mountPoint, $storageBackend, $authBackend, $priority, $type) {
197
		if (!$priority) {
198
			$priority = 100;
199
		}
200
		$builder = $this->connection->getQueryBuilder();
201
		$query = $builder->insert('external_mounts')
202
			->values([
203
				'mount_point' => $builder->createNamedParameter($mountPoint, IQueryBuilder::PARAM_STR),
204
				'storage_backend' => $builder->createNamedParameter($storageBackend, IQueryBuilder::PARAM_STR),
205
				'auth_backend' => $builder->createNamedParameter($authBackend, IQueryBuilder::PARAM_STR),
206
				'priority' => $builder->createNamedParameter($priority, IQueryBuilder::PARAM_INT),
207
				'type' => $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)
208
			]);
209
		$query->execute();
210
		return (int)$this->connection->lastInsertId('*PREFIX*external_mounts');
211
	}
212
213
	/**
214
	 * Remove a mount from the database
215
	 *
216
	 * @param int $mountId
217
	 */
218
	public function removeMount($mountId) {
219
		$builder = $this->connection->getQueryBuilder();
220
		$query = $builder->delete('external_mounts')
221
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
222
		$query->execute();
223
224
		$query = $builder->delete('external_applicable')
225
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
226
		$query->execute();
227
228
		$query = $builder->delete('external_config')
229
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
230
		$query->execute();
231
232
		$query = $builder->delete('external_options')
233
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
234
		$query->execute();
235
	}
236
237
	/**
238
	 * @param int $mountId
239
	 * @param string $newMountPoint
240
	 */
241 View Code Duplication
	public function setMountPoint($mountId, $newMountPoint) {
242
		$builder = $this->connection->getQueryBuilder();
243
244
		$query = $builder->update('external_mounts')
245
			->set('mount_point', $builder->createNamedParameter($newMountPoint))
246
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
247
248
		$query->execute();
249
	}
250
251
	/**
252
	 * @param int $mountId
253
	 * @param string $newAuthBackend
254
	 */
255 View Code Duplication
	public function setAuthBackend($mountId, $newAuthBackend) {
256
		$builder = $this->connection->getQueryBuilder();
257
258
		$query = $builder->update('external_mounts')
259
			->set('auth_backend', $builder->createNamedParameter($newAuthBackend))
260
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)));
261
262
		$query->execute();
263
	}
264
265
	/**
266
	 * @param int $mountId
267
	 * @param string $key
268
	 * @param string $value
269
	 */
270
	public function setConfig($mountId, $key, $value) {
271
		if ($key === 'password') {
272
			$value = $this->encryptValue($value);
273
		}
274
		$count = $this->connection->insertIfNotExist('*PREFIX*external_config', [
275
			'mount_id' => $mountId,
276
			'key' => $key,
277
			'value' => $value
278
		], ['mount_id', 'key']);
279 View Code Duplication
		if ($count === 0) {
0 ignored issues
show
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...
280
			$builder = $this->connection->getQueryBuilder();
281
			$query = $builder->update('external_config')
282
				->set('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR))
283
				->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
284
				->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
285
			$query->execute();
286
		}
287
	}
288
289
	/**
290
	 * @param int $mountId
291
	 * @param string $key
292
	 * @param string $value
293
	 */
294
	public function setOption($mountId, $key, $value) {
295
296
		$count = $this->connection->insertIfNotExist('*PREFIX*external_options', [
297
			'mount_id' => $mountId,
298
			'key' => $key,
299
			'value' => json_encode($value)
300
		], ['mount_id', 'key']);
301 View Code Duplication
		if ($count === 0) {
0 ignored issues
show
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...
302
			$builder = $this->connection->getQueryBuilder();
303
			$query = $builder->update('external_options')
304
				->set('value', $builder->createNamedParameter(json_encode($value), IQueryBuilder::PARAM_STR))
305
				->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
306
				->andWhere($builder->expr()->eq('key', $builder->createNamedParameter($key, IQueryBuilder::PARAM_STR)));
307
			$query->execute();
308
		}
309
	}
310
311
	public function addApplicable($mountId, $type, $value) {
312
		$this->connection->insertIfNotExist('*PREFIX*external_applicable', [
313
			'mount_id' => $mountId,
314
			'type' => $type,
315
			'value' => $value
316
		], ['mount_id', 'type', 'value']);
317
	}
318
319
	public function removeApplicable($mountId, $type, $value) {
320
		$builder = $this->connection->getQueryBuilder();
321
		$query = $builder->delete('external_applicable')
322
			->where($builder->expr()->eq('mount_id', $builder->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
323
			->andWhere($builder->expr()->eq('type', $builder->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
324
325 View Code Duplication
		if (is_null($value)) {
326
			$query = $query->andWhere($builder->expr()->isNull('value'));
327
		} else {
328
			$query = $query->andWhere($builder->expr()->eq('value', $builder->createNamedParameter($value, IQueryBuilder::PARAM_STR)));
329
		}
330
331
		$query->execute();
332
	}
333
334
	private function getMountsFromQuery(IQueryBuilder $query) {
335
		$result = $query->execute();
336
		$mounts = $result->fetchAll();
337
		$uniqueMounts = [];
338
		foreach ($mounts as $mount) {
339
			$id = $mount['mount_id'];
340
			if (!isset($uniqueMounts[$id])) {
341
				$uniqueMounts[$id] = $mount;
342
			}
343
		}
344
		$uniqueMounts = array_values($uniqueMounts);
345
346
		$mountIds = array_map(function ($mount) {
347
			return $mount['mount_id'];
348
		}, $uniqueMounts);
349
		$mountIds = array_values(array_unique($mountIds));
350
351
		$applicable = $this->getApplicableForMounts($mountIds);
352
		$config = $this->getConfigForMounts($mountIds);
353
		$options = $this->getOptionsForMounts($mountIds);
354
355
		return array_map(function ($mount, $applicable, $config, $options) {
356
			$mount['type'] = (int)$mount['type'];
357
			$mount['priority'] = (int)$mount['priority'];
358
			$mount['applicable'] = $applicable;
359
			$mount['config'] = $config;
360
			$mount['options'] = $options;
361
			return $mount;
362
		}, $uniqueMounts, $applicable, $config, $options);
363
	}
364
365
	/**
366
	 * Get mount options from a table grouped by mount id
367
	 *
368
	 * @param string $table
369
	 * @param string[] $fields
370
	 * @param int[] $mountIds
371
	 * @return array [$mountId => [['field1' => $value1, ...], ...], ...]
372
	 */
373
	private function selectForMounts($table, array $fields, array $mountIds) {
374
		if (count($mountIds) === 0) {
375
			return [];
376
		}
377
		$builder = $this->connection->getQueryBuilder();
378
		$fields[] = 'mount_id';
379
		$placeHolders = array_map(function ($id) use ($builder) {
380
			return $builder->createPositionalParameter($id, IQueryBuilder::PARAM_INT);
381
		}, $mountIds);
382
		$query = $builder->select($fields)
383
			->from($table)
384
			->where($builder->expr()->in('mount_id', $placeHolders));
385
		$rows = $query->execute()->fetchAll();
386
387
		$result = [];
388
		foreach ($mountIds as $mountId) {
389
			$result[$mountId] = [];
390
		}
391
		foreach ($rows as $row) {
392
			if (isset($row['type'])) {
393
				$row['type'] = (int)$row['type'];
394
			}
395
			$result[$row['mount_id']][] = $row;
396
		}
397
		return $result;
398
	}
399
400
	/**
401
	 * @param int[] $mountIds
402
	 * @return array [$id => [['type' => $type, 'value' => $value], ...], ...]
403
	 */
404
	public function getApplicableForMounts($mountIds) {
405
		return $this->selectForMounts('external_applicable', ['type', 'value'], $mountIds);
406
	}
407
408
	/**
409
	 * @param int[] $mountIds
410
	 * @return array [$id => ['key1' => $value1, ...], ...]
411
	 */
412
	public function getConfigForMounts($mountIds) {
413
		$mountConfigs = $this->selectForMounts('external_config', ['key', 'value'], $mountIds);
414
		return array_map([$this, 'createKeyValueMap'], $mountConfigs);
415
	}
416
417
	/**
418
	 * @param int[] $mountIds
419
	 * @return array [$id => ['key1' => $value1, ...], ...]
420
	 */
421
	public function getOptionsForMounts($mountIds) {
422
		$mountOptions = $this->selectForMounts('external_options', ['key', 'value'], $mountIds);
423
		$optionsMap = array_map([$this, 'createKeyValueMap'], $mountOptions);
424
		return array_map(function (array $options) {
425
			return array_map(function ($option) {
426
				return json_decode($option);
427
			}, $options);
428
		}, $optionsMap);
429
	}
430
431
	/**
432
	 * @param array $keyValuePairs [['key'=>$key, 'value=>$value], ...]
433
	 * @return array ['key1' => $value1, ...]
434
	 */
435
	private function createKeyValueMap(array $keyValuePairs) {
436
		$decryptedPairts = array_map(function ($pair) {
437
			if ($pair['key'] === 'password') {
438
				$pair['value'] = $this->decryptValue($pair['value']);
439
			}
440
			return $pair;
441
		}, $keyValuePairs);
442
		$keys = array_map(function ($pair) {
443
			return $pair['key'];
444
		}, $decryptedPairts);
445
		$values = array_map(function ($pair) {
446
			return $pair['value'];
447
		}, $decryptedPairts);
448
449
		return array_combine($keys, $values);
450
	}
451
452
	private function encryptValue($value) {
453
		return $this->crypto->encrypt($value);
454
	}
455
456
	private function decryptValue($value) {
457
		try {
458
			return $this->crypto->decrypt($value);
459
		} catch (\Exception $e) {
460
			return $value;
461
		}
462
	}
463
}
464