Passed
Push — master ( 1c68a6...4bf891 )
by Morris
13:49 queued 10s
created

AppConfig::getFilteredValues()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 2
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2017, Joas Schilling <[email protected]>
4
 * @copyright Copyright (c) 2016, ownCloud, Inc.
5
 *
6
 * @author Arthur Schiwon <[email protected]>
7
 * @author Bart Visscher <[email protected]>
8
 * @author Christoph Wurst <[email protected]>
9
 * @author Jakob Sack <[email protected]>
10
 * @author Joas Schilling <[email protected]>
11
 * @author Jörn Friedrich Dreyer <[email protected]>
12
 * @author michaelletzgus <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Robin McCorkell <[email protected]>
16
 *
17
 * @license AGPL-3.0
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program. If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
33
namespace OC;
34
35
use OC\DB\OracleConnection;
36
use OCP\IAppConfig;
37
use OCP\IConfig;
38
use OCP\IDBConnection;
39
40
/**
41
 * This class provides an easy way for apps to store config values in the
42
 * database.
43
 */
44
class AppConfig implements IAppConfig {
45
46
	/** @var array[] */
47
	protected $sensitiveValues = [
48
		'external' => [
49
			'/^sites$/',
50
		],
51
		'spreed' => [
52
			'/^bridge_bot_password/',
53
			'/^signaling_servers$/',
54
			'/^signaling_ticket_secret$/',
55
			'/^stun_servers$/',
56
			'/^turn_servers$/',
57
			'/^turn_server_secret$/',
58
		],
59
		'theming' => [
60
			'/^imprintUrl$/',
61
			'/^privacyUrl$/',
62
			'/^slogan$/',
63
			'/^url$/',
64
		],
65
		'user_ldap' => [
66
			'/^(s..)?ldap_agent_password$/',
67
		],
68
	];
69
70
	/** @var \OCP\IDBConnection */
71
	protected $conn;
72
73
	/** @var array[] */
74
	private $cache = [];
75
76
	/** @var bool */
77
	private $configLoaded = false;
78
79
	/**
80
	 * @param IDBConnection $conn
81
	 */
82
	public function __construct(IDBConnection $conn) {
83
		$this->conn = $conn;
84
		$this->configLoaded = false;
85
	}
86
87
	/**
88
	 * @param string $app
89
	 * @return array
90
	 */
91
	private function getAppValues($app) {
92
		$this->loadConfigValues();
93
94
		if (isset($this->cache[$app])) {
95
			return $this->cache[$app];
96
		}
97
98
		return [];
99
	}
100
101
	/**
102
	 * Get all apps using the config
103
	 *
104
	 * @return array an array of app ids
105
	 *
106
	 * This function returns a list of all apps that have at least one
107
	 * entry in the appconfig table.
108
	 */
109
	public function getApps() {
110
		$this->loadConfigValues();
111
112
		return $this->getSortedKeys($this->cache);
113
	}
114
115
	/**
116
	 * Get the available keys for an app
117
	 *
118
	 * @param string $app the app we are looking for
119
	 * @return array an array of key names
120
	 *
121
	 * This function gets all keys of an app. Please note that the values are
122
	 * not returned.
123
	 */
124
	public function getKeys($app) {
125
		$this->loadConfigValues();
126
127
		if (isset($this->cache[$app])) {
128
			return $this->getSortedKeys($this->cache[$app]);
129
		}
130
131
		return [];
132
	}
133
134
	public function getSortedKeys($data) {
135
		$keys = array_keys($data);
136
		sort($keys);
137
		return $keys;
138
	}
139
140
	/**
141
	 * Gets the config value
142
	 *
143
	 * @param string $app app
144
	 * @param string $key key
145
	 * @param string $default = null, default value if the key does not exist
146
	 * @return string the value or $default
147
	 *
148
	 * This function gets a value from the appconfig table. If the key does
149
	 * not exist the default value will be returned
150
	 */
151
	public function getValue($app, $key, $default = null) {
152
		$this->loadConfigValues();
153
154
		if ($this->hasKey($app, $key)) {
155
			return $this->cache[$app][$key];
156
		}
157
158
		return $default;
159
	}
160
161
	/**
162
	 * check if a key is set in the appconfig
163
	 *
164
	 * @param string $app
165
	 * @param string $key
166
	 * @return bool
167
	 */
168
	public function hasKey($app, $key) {
169
		$this->loadConfigValues();
170
171
		return isset($this->cache[$app][$key]);
172
	}
173
174
	/**
175
	 * Sets a value. If the key did not exist before it will be created.
176
	 *
177
	 * @param string $app app
178
	 * @param string $key key
179
	 * @param string|float|int $value value
180
	 * @return bool True if the value was inserted or updated, false if the value was the same
181
	 */
182
	public function setValue($app, $key, $value) {
183
		if (!$this->hasKey($app, $key)) {
184
			$inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [
185
				'appid' => $app,
186
				'configkey' => $key,
187
				'configvalue' => $value,
188
			], [
189
				'appid',
190
				'configkey',
191
			]);
192
193
			if ($inserted) {
194
				if (!isset($this->cache[$app])) {
195
					$this->cache[$app] = [];
196
				}
197
198
				$this->cache[$app][$key] = $value;
199
				return true;
200
			}
201
		}
202
203
		$sql = $this->conn->getQueryBuilder();
204
		$sql->update('appconfig')
205
			->set('configvalue', $sql->createParameter('configvalue'))
206
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
207
			->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
208
			->setParameter('configvalue', $value)
209
			->setParameter('app', $app)
210
			->setParameter('configkey', $key);
211
212
		/*
213
		 * Only limit to the existing value for non-Oracle DBs:
214
		 * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286
215
		 * > Large objects (LOBs) are not supported in comparison conditions.
216
		 */
217
		if (!($this->conn instanceof OracleConnection)) {
218
			// Only update the value when it is not the same
219
			$sql->andWhere($sql->expr()->neq('configvalue', $sql->createParameter('configvalue')))
220
				->setParameter('configvalue', $value);
221
		}
222
223
		$changedRow = (bool) $sql->execute();
224
225
		$this->cache[$app][$key] = $value;
226
227
		return $changedRow;
228
	}
229
230
	/**
231
	 * Deletes a key
232
	 *
233
	 * @param string $app app
234
	 * @param string $key key
235
	 * @return boolean
236
	 */
237
	public function deleteKey($app, $key) {
238
		$this->loadConfigValues();
239
240
		$sql = $this->conn->getQueryBuilder();
241
		$sql->delete('appconfig')
242
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
243
			->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
244
			->setParameter('app', $app)
245
			->setParameter('configkey', $key);
246
		$sql->execute();
247
248
		unset($this->cache[$app][$key]);
249
		return false;
250
	}
251
252
	/**
253
	 * Remove app from appconfig
254
	 *
255
	 * @param string $app app
256
	 * @return boolean
257
	 *
258
	 * Removes all keys in appconfig belonging to the app.
259
	 */
260
	public function deleteApp($app) {
261
		$this->loadConfigValues();
262
263
		$sql = $this->conn->getQueryBuilder();
264
		$sql->delete('appconfig')
265
			->where($sql->expr()->eq('appid', $sql->createParameter('app')))
266
			->setParameter('app', $app);
267
		$sql->execute();
268
269
		unset($this->cache[$app]);
270
		return false;
271
	}
272
273
	/**
274
	 * get multiple values, either the app or key can be used as wildcard by setting it to false
275
	 *
276
	 * @param string|false $app
277
	 * @param string|false $key
278
	 * @return array|false
279
	 */
280
	public function getValues($app, $key) {
281
		if (($app !== false) === ($key !== false)) {
282
			return false;
283
		}
284
285
		if ($key === false) {
286
			return $this->getAppValues($app);
287
		} else {
288
			$appIds = $this->getApps();
289
			$values = array_map(function ($appId) use ($key) {
290
				return isset($this->cache[$appId][$key]) ? $this->cache[$appId][$key] : null;
291
			}, $appIds);
292
			$result = array_combine($appIds, $values);
293
294
			return array_filter($result);
295
		}
296
	}
297
298
	/**
299
	 * get all values of the app or and filters out sensitive data
300
	 *
301
	 * @param string $app
302
	 * @return array
303
	 */
304
	public function getFilteredValues($app) {
305
		$values = $this->getValues($app, false);
306
307
		if (isset($this->sensitiveValues[$app])) {
308
			foreach ($this->sensitiveValues[$app] as $sensitiveKeyExp) {
309
				$sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values));
310
				foreach ($sensitiveKeys as $sensitiveKey) {
311
					$values[$sensitiveKey] = IConfig::SENSITIVE_VALUE;
312
				}
313
			}
314
		}
315
316
		return $values;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $values could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
317
	}
318
319
	/**
320
	 * Load all the app config values
321
	 */
322
	protected function loadConfigValues() {
323
		if ($this->configLoaded) {
324
			return;
325
		}
326
327
		$this->cache = [];
328
329
		$sql = $this->conn->getQueryBuilder();
330
		$sql->select('*')
331
			->from('appconfig');
332
		$result = $sql->execute();
333
334
		// we are going to store the result in memory anyway
335
		$rows = $result->fetchAll();
336
		foreach ($rows as $row) {
337
			if (!isset($this->cache[$row['appid']])) {
338
				$this->cache[$row['appid']] = [];
339
			}
340
341
			$this->cache[$row['appid']][$row['configkey']] = $row['configvalue'];
342
		}
343
		$result->closeCursor();
344
345
		$this->configLoaded = true;
346
	}
347
348
	/**
349
	 * Clear all the cached app config values
350
	 *
351
	 * WARNING: do not use this - this is only for usage with the SCSSCacher to
352
	 * clear the memory cache of the app config
353
	 */
354
	public function clearCachedConfig() {
355
		$this->configLoaded = false;
356
	}
357
}
358