You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AppConfig.php 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com>
  5. * @copyright Copyright (c) 2016, ownCloud, Inc.
  6. *
  7. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  8. * @author Bart Visscher <bartv@thisnet.nl>
  9. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  10. * @author Jakob Sack <mail@jakobsack.de>
  11. * @author Joas Schilling <coding@schilljs.com>
  12. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  13. * @author Maxence Lange <maxence@artificial-owl.com>
  14. * @author michaelletzgus <michaelletzgus@users.noreply.github.com>
  15. * @author Morris Jobke <hey@morrisjobke.de>
  16. * @author Robin Appelman <robin@icewind.nl>
  17. * @author Robin McCorkell <robin@mccorkell.me.uk>
  18. * @author Roeland Jago Douma <roeland@famdouma.nl>
  19. *
  20. * @license AGPL-3.0
  21. *
  22. * This code is free software: you can redistribute it and/or modify
  23. * it under the terms of the GNU Affero General Public License, version 3,
  24. * as published by the Free Software Foundation.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU Affero General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Affero General Public License, version 3,
  32. * along with this program. If not, see <http://www.gnu.org/licenses/>
  33. *
  34. */
  35. namespace OC;
  36. use InvalidArgumentException;
  37. use JsonException;
  38. use OCP\DB\Exception as DBException;
  39. use OCP\DB\QueryBuilder\IQueryBuilder;
  40. use OCP\Exceptions\AppConfigIncorrectTypeException;
  41. use OCP\Exceptions\AppConfigTypeConflictException;
  42. use OCP\Exceptions\AppConfigUnknownKeyException;
  43. use OCP\IAppConfig;
  44. use OCP\IConfig;
  45. use OCP\IDBConnection;
  46. use Psr\Log\LoggerInterface;
  47. /**
  48. * This class provides an easy way for apps to store config values in the
  49. * database.
  50. *
  51. * **Note:** since 29.0.0, it supports **lazy loading**
  52. *
  53. * ### What is lazy loading ?
  54. * In order to avoid loading useless config values into memory for each request,
  55. * only non-lazy values are now loaded.
  56. *
  57. * Once a value that is lazy is requested, all lazy values will be loaded.
  58. *
  59. * Similarly, some methods from this class are marked with a warning about ignoring
  60. * lazy loading. Use them wisely and only on parts of the code that are called
  61. * during specific requests or actions to avoid loading the lazy values all the time.
  62. *
  63. * @since 7.0.0
  64. * @since 29.0.0 - Supporting types and lazy loading
  65. */
  66. class AppConfig implements IAppConfig {
  67. private const APP_MAX_LENGTH = 32;
  68. private const KEY_MAX_LENGTH = 64;
  69. /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */
  70. private array $fastCache = []; // cache for normal config keys
  71. /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */
  72. private array $lazyCache = []; // cache for lazy config keys
  73. /** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */
  74. private array $valueTypes = []; // type for all config values
  75. private bool $fastLoaded = false;
  76. private bool $lazyLoaded = false;
  77. /**
  78. * $migrationCompleted is only needed to manage the previous structure
  79. * of the database during the upgrading process to nc29.
  80. *
  81. * only when upgrading from a version prior 28.0.2
  82. *
  83. * @TODO: remove this value in Nextcloud 30+
  84. */
  85. private bool $migrationCompleted = true;
  86. public function __construct(
  87. protected IDBConnection $connection,
  88. private LoggerInterface $logger,
  89. ) {
  90. }
  91. /**
  92. * @inheritDoc
  93. *
  94. * @return string[] list of app ids
  95. * @since 7.0.0
  96. */
  97. public function getApps(): array {
  98. $this->loadConfigAll();
  99. $apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache));
  100. sort($apps);
  101. return array_values(array_unique($apps));
  102. }
  103. /**
  104. * @inheritDoc
  105. *
  106. * @param string $app id of the app
  107. *
  108. * @return string[] list of stored config keys
  109. * @since 29.0.0
  110. */
  111. public function getKeys(string $app): array {
  112. $this->assertParams($app);
  113. $this->loadConfigAll();
  114. $keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? []));
  115. sort($keys);
  116. return array_values(array_unique($keys));
  117. }
  118. /**
  119. * @inheritDoc
  120. *
  121. * @param string $app id of the app
  122. * @param string $key config key
  123. * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
  124. *
  125. * @return bool TRUE if key exists
  126. * @since 7.0.0
  127. * @since 29.0.0 Added the $lazy argument
  128. */
  129. public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
  130. $this->assertParams($app, $key);
  131. $this->loadConfig($lazy);
  132. if ($lazy === null) {
  133. $appCache = $this->getAllValues($app);
  134. return isset($appCache[$key]);
  135. }
  136. if ($lazy) {
  137. return isset($this->lazyCache[$app][$key]);
  138. }
  139. return isset($this->fastCache[$app][$key]);
  140. }
  141. /**
  142. * @param string $app id of the app
  143. * @param string $key config key
  144. * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
  145. *
  146. * @return bool
  147. * @throws AppConfigUnknownKeyException if config key is not known
  148. * @since 29.0.0
  149. */
  150. public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
  151. $this->assertParams($app, $key);
  152. $this->loadConfig($lazy);
  153. if (!isset($this->valueTypes[$app][$key])) {
  154. throw new AppConfigUnknownKeyException('unknown config key');
  155. }
  156. return $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key]);
  157. }
  158. /**
  159. * @inheritDoc
  160. *
  161. * @param string $app if of the app
  162. * @param string $key config key
  163. *
  164. * @return bool TRUE if config is lazy loaded
  165. * @throws AppConfigUnknownKeyException if config key is not known
  166. * @see IAppConfig for details about lazy loading
  167. * @since 29.0.0
  168. */
  169. public function isLazy(string $app, string $key): bool {
  170. // there is a huge probability the non-lazy config are already loaded
  171. if ($this->hasKey($app, $key, false)) {
  172. return false;
  173. }
  174. // key not found, we search in the lazy config
  175. if ($this->hasKey($app, $key, true)) {
  176. return true;
  177. }
  178. throw new AppConfigUnknownKeyException('unknown config key');
  179. }
  180. /**
  181. * @inheritDoc
  182. *
  183. * @param string $app id of the app
  184. * @param string $key config keys prefix to search
  185. * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE}
  186. *
  187. * @return array<string, string> [configKey => configValue]
  188. * @since 29.0.0
  189. */
  190. public function getAllValues(string $app, string $key = '', bool $filtered = false): array {
  191. $this->assertParams($app, $key);
  192. // if we want to filter values, we need to get sensitivity
  193. $this->loadConfigAll();
  194. // array_merge() will remove numeric keys (here config keys), so addition arrays instead
  195. $values = ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? []);
  196. if (!$filtered) {
  197. return $values;
  198. }
  199. /**
  200. * Using the old (deprecated) list of sensitive values.
  201. */
  202. foreach ($this->getSensitiveKeys($app) as $sensitiveKeyExp) {
  203. $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values));
  204. foreach ($sensitiveKeys as $sensitiveKey) {
  205. $this->valueTypes[$app][$sensitiveKey] = ($this->valueTypes[$app][$sensitiveKey] ?? 0) | self::VALUE_SENSITIVE;
  206. }
  207. }
  208. $result = [];
  209. foreach ($values as $key => $value) {
  210. $result[$key] = $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key] ?? 0) ? IConfig::SENSITIVE_VALUE : $value;
  211. }
  212. return $result;
  213. }
  214. /**
  215. * @inheritDoc
  216. *
  217. * @param string $key config key
  218. * @param bool $lazy search within lazy loaded config
  219. *
  220. * @return array<string, string> [appId => configValue]
  221. * @since 29.0.0
  222. */
  223. public function searchValues(string $key, bool $lazy = false): array {
  224. $this->assertParams('', $key, true);
  225. $this->loadConfig($lazy);
  226. $values = [];
  227. /** @var array<array-key, array<array-key, mixed>> $cache */
  228. if ($lazy) {
  229. $cache = $this->lazyCache;
  230. } else {
  231. $cache = $this->fastCache;
  232. }
  233. foreach (array_keys($cache) as $app) {
  234. if (isset($cache[$app][$key])) {
  235. $values[$app] = $cache[$app][$key];
  236. }
  237. }
  238. return $values;
  239. }
  240. /**
  241. * Get the config value as string.
  242. * If the value does not exist the given default will be returned.
  243. *
  244. * Set lazy to `null` to ignore it and get the value from either source.
  245. *
  246. * **WARNING:** Method is internal and **SHOULD** not be used, as it is better to get the value with a type.
  247. *
  248. * @param string $app id of the app
  249. * @param string $key config key
  250. * @param string $default config value
  251. * @param null|bool $lazy get config as lazy loaded or not. can be NULL
  252. *
  253. * @return string the value or $default
  254. * @internal
  255. * @since 29.0.0
  256. * @see IAppConfig for explanation about lazy loading
  257. * @see getValueString()
  258. * @see getValueInt()
  259. * @see getValueFloat()
  260. * @see getValueBool()
  261. * @see getValueArray()
  262. */
  263. public function getValueMixed(
  264. string $app,
  265. string $key,
  266. string $default = '',
  267. ?bool $lazy = false
  268. ): string {
  269. try {
  270. $lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy;
  271. } catch (AppConfigUnknownKeyException $e) {
  272. return $default;
  273. }
  274. return $this->getTypedValue(
  275. $app,
  276. $key,
  277. $default,
  278. $lazy,
  279. self::VALUE_MIXED
  280. );
  281. }
  282. /**
  283. * @inheritDoc
  284. *
  285. * @param string $app id of the app
  286. * @param string $key config key
  287. * @param string $default default value
  288. * @param bool $lazy search within lazy loaded config
  289. *
  290. * @return string stored config value or $default if not set in database
  291. * @throws InvalidArgumentException if one of the argument format is invalid
  292. * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
  293. * @since 29.0.0
  294. * @see IAppConfig for explanation about lazy loading
  295. */
  296. public function getValueString(
  297. string $app,
  298. string $key,
  299. string $default = '',
  300. bool $lazy = false
  301. ): string {
  302. return $this->getTypedValue($app, $key, $default, $lazy, self::VALUE_STRING);
  303. }
  304. /**
  305. * @inheritDoc
  306. *
  307. * @param string $app id of the app
  308. * @param string $key config key
  309. * @param int $default default value
  310. * @param bool $lazy search within lazy loaded config
  311. *
  312. * @return int stored config value or $default if not set in database
  313. * @throws InvalidArgumentException if one of the argument format is invalid
  314. * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
  315. * @since 29.0.0
  316. * @see IAppConfig for explanation about lazy loading
  317. */
  318. public function getValueInt(
  319. string $app,
  320. string $key,
  321. int $default = 0,
  322. bool $lazy = false
  323. ): int {
  324. return (int)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_INT);
  325. }
  326. /**
  327. * @inheritDoc
  328. *
  329. * @param string $app id of the app
  330. * @param string $key config key
  331. * @param float $default default value
  332. * @param bool $lazy search within lazy loaded config
  333. *
  334. * @return float stored config value or $default if not set in database
  335. * @throws InvalidArgumentException if one of the argument format is invalid
  336. * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
  337. * @since 29.0.0
  338. * @see IAppConfig for explanation about lazy loading
  339. */
  340. public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float {
  341. return (float)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_FLOAT);
  342. }
  343. /**
  344. * @inheritDoc
  345. *
  346. * @param string $app id of the app
  347. * @param string $key config key
  348. * @param bool $default default value
  349. * @param bool $lazy search within lazy loaded config
  350. *
  351. * @return bool stored config value or $default if not set in database
  352. * @throws InvalidArgumentException if one of the argument format is invalid
  353. * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
  354. * @since 29.0.0
  355. * @see IAppConfig for explanation about lazy loading
  356. */
  357. public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool {
  358. $b = strtolower($this->getTypedValue($app, $key, $default ? 'true' : 'false', $lazy, self::VALUE_BOOL));
  359. return in_array($b, ['1', 'true', 'yes', 'on']);
  360. }
  361. /**
  362. * @inheritDoc
  363. *
  364. * @param string $app id of the app
  365. * @param string $key config key
  366. * @param array $default default value
  367. * @param bool $lazy search within lazy loaded config
  368. *
  369. * @return array stored config value or $default if not set in database
  370. * @throws InvalidArgumentException if one of the argument format is invalid
  371. * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
  372. * @since 29.0.0
  373. * @see IAppConfig for explanation about lazy loading
  374. */
  375. public function getValueArray(
  376. string $app,
  377. string $key,
  378. array $default = [],
  379. bool $lazy = false
  380. ): array {
  381. try {
  382. $defaultJson = json_encode($default, JSON_THROW_ON_ERROR);
  383. $value = json_decode($this->getTypedValue($app, $key, $defaultJson, $lazy, self::VALUE_ARRAY), true, flags: JSON_THROW_ON_ERROR);
  384. return is_array($value) ? $value : [];
  385. } catch (JsonException) {
  386. return [];
  387. }
  388. }
  389. /**
  390. * @param string $app id of the app
  391. * @param string $key config key
  392. * @param string $default default value
  393. * @param bool $lazy search within lazy loaded config
  394. * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT}{@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
  395. *
  396. * @return string
  397. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  398. * @throws InvalidArgumentException
  399. */
  400. private function getTypedValue(
  401. string $app,
  402. string $key,
  403. string $default,
  404. bool $lazy,
  405. int $type
  406. ): string {
  407. $this->assertParams($app, $key, valueType: $type);
  408. $this->loadConfig($lazy);
  409. /**
  410. * We ignore check if mixed type is requested.
  411. * If type of stored value is set as mixed, we don't filter.
  412. * If type of stored value is defined, we compare with the one requested.
  413. */
  414. $knownType = $this->valueTypes[$app][$key] ?? 0;
  415. if (!$this->isTyped(self::VALUE_MIXED, $type)
  416. && $knownType > 0
  417. && !$this->isTyped(self::VALUE_MIXED, $knownType)
  418. && !$this->isTyped($type, $knownType)) {
  419. $this->logger->warning('conflict with value type from database', ['app' => $app, 'key' => $key, 'type' => $type, 'knownType' => $knownType]);
  420. throw new AppConfigTypeConflictException('conflict with value type from database');
  421. }
  422. if ($lazy) {
  423. return $this->lazyCache[$app][$key] ?? $default;
  424. }
  425. return $this->fastCache[$app][$key] ?? $default;
  426. }
  427. /**
  428. * @inheritDoc
  429. *
  430. * @param string $app id of the app
  431. * @param string $key config key
  432. *
  433. * @return int type of the value
  434. * @throws AppConfigUnknownKeyException if config key is not known
  435. * @since 29.0.0
  436. * @see VALUE_STRING
  437. * @see VALUE_INT
  438. * @see VALUE_FLOAT
  439. * @see VALUE_BOOL
  440. * @see VALUE_ARRAY
  441. */
  442. public function getValueType(string $app, string $key): int {
  443. $this->assertParams($app, $key);
  444. $this->loadConfigAll();
  445. if (!isset($this->valueTypes[$app][$key])) {
  446. throw new AppConfigUnknownKeyException('unknown config key');
  447. }
  448. $type = $this->valueTypes[$app][$key];
  449. $type &= ~self::VALUE_SENSITIVE;
  450. return $type;
  451. }
  452. /**
  453. * Store a config key and its value in database as VALUE_MIXED
  454. *
  455. * **WARNING:** Method is internal and **MUST** not be used as it is best to set a real value type
  456. *
  457. * @param string $app id of the app
  458. * @param string $key config key
  459. * @param string $value config value
  460. * @param bool $lazy set config as lazy loaded
  461. * @param bool $sensitive if TRUE value will be hidden when listing config values.
  462. *
  463. * @return bool TRUE if value was different, therefor updated in database
  464. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED
  465. * @internal
  466. * @since 29.0.0
  467. * @see IAppConfig for explanation about lazy loading
  468. * @see setValueString()
  469. * @see setValueInt()
  470. * @see setValueFloat()
  471. * @see setValueBool()
  472. * @see setValueArray()
  473. */
  474. public function setValueMixed(
  475. string $app,
  476. string $key,
  477. string $value,
  478. bool $lazy = false,
  479. bool $sensitive = false
  480. ): bool {
  481. return $this->setTypedValue(
  482. $app,
  483. $key,
  484. $value,
  485. $lazy,
  486. self::VALUE_MIXED | ($sensitive ? self::VALUE_SENSITIVE : 0)
  487. );
  488. }
  489. /**
  490. * @inheritDoc
  491. *
  492. * @param string $app id of the app
  493. * @param string $key config key
  494. * @param string $value config value
  495. * @param bool $lazy set config as lazy loaded
  496. * @param bool $sensitive if TRUE value will be hidden when listing config values.
  497. *
  498. * @return bool TRUE if value was different, therefor updated in database
  499. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  500. * @since 29.0.0
  501. * @see IAppConfig for explanation about lazy loading
  502. */
  503. public function setValueString(
  504. string $app,
  505. string $key,
  506. string $value,
  507. bool $lazy = false,
  508. bool $sensitive = false
  509. ): bool {
  510. return $this->setTypedValue(
  511. $app,
  512. $key,
  513. $value,
  514. $lazy,
  515. self::VALUE_STRING | ($sensitive ? self::VALUE_SENSITIVE : 0)
  516. );
  517. }
  518. /**
  519. * @inheritDoc
  520. *
  521. * @param string $app id of the app
  522. * @param string $key config key
  523. * @param int $value config value
  524. * @param bool $lazy set config as lazy loaded
  525. * @param bool $sensitive if TRUE value will be hidden when listing config values.
  526. *
  527. * @return bool TRUE if value was different, therefor updated in database
  528. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  529. * @since 29.0.0
  530. * @see IAppConfig for explanation about lazy loading
  531. */
  532. public function setValueInt(
  533. string $app,
  534. string $key,
  535. int $value,
  536. bool $lazy = false,
  537. bool $sensitive = false
  538. ): bool {
  539. if ($value > 2000000000) {
  540. $this->logger->debug('You are trying to store an integer value around/above 2,147,483,647. This is a reminder that reaching this theoretical limit on 32 bits system will throw an exception.');
  541. }
  542. return $this->setTypedValue(
  543. $app,
  544. $key,
  545. (string)$value,
  546. $lazy,
  547. self::VALUE_INT | ($sensitive ? self::VALUE_SENSITIVE : 0)
  548. );
  549. }
  550. /**
  551. * @inheritDoc
  552. *
  553. * @param string $app id of the app
  554. * @param string $key config key
  555. * @param float $value config value
  556. * @param bool $lazy set config as lazy loaded
  557. * @param bool $sensitive if TRUE value will be hidden when listing config values.
  558. *
  559. * @return bool TRUE if value was different, therefor updated in database
  560. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  561. * @since 29.0.0
  562. * @see IAppConfig for explanation about lazy loading
  563. */
  564. public function setValueFloat(
  565. string $app,
  566. string $key,
  567. float $value,
  568. bool $lazy = false,
  569. bool $sensitive = false
  570. ): bool {
  571. return $this->setTypedValue(
  572. $app,
  573. $key,
  574. (string)$value,
  575. $lazy,
  576. self::VALUE_FLOAT | ($sensitive ? self::VALUE_SENSITIVE : 0)
  577. );
  578. }
  579. /**
  580. * @inheritDoc
  581. *
  582. * @param string $app id of the app
  583. * @param string $key config key
  584. * @param bool $value config value
  585. * @param bool $lazy set config as lazy loaded
  586. *
  587. * @return bool TRUE if value was different, therefor updated in database
  588. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  589. * @since 29.0.0
  590. * @see IAppConfig for explanation about lazy loading
  591. */
  592. public function setValueBool(
  593. string $app,
  594. string $key,
  595. bool $value,
  596. bool $lazy = false
  597. ): bool {
  598. return $this->setTypedValue(
  599. $app,
  600. $key,
  601. ($value) ? '1' : '0',
  602. $lazy,
  603. self::VALUE_BOOL
  604. );
  605. }
  606. /**
  607. * @inheritDoc
  608. *
  609. * @param string $app id of the app
  610. * @param string $key config key
  611. * @param array $value config value
  612. * @param bool $lazy set config as lazy loaded
  613. * @param bool $sensitive if TRUE value will be hidden when listing config values.
  614. *
  615. * @return bool TRUE if value was different, therefor updated in database
  616. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  617. * @throws JsonException
  618. * @since 29.0.0
  619. * @see IAppConfig for explanation about lazy loading
  620. */
  621. public function setValueArray(
  622. string $app,
  623. string $key,
  624. array $value,
  625. bool $lazy = false,
  626. bool $sensitive = false
  627. ): bool {
  628. try {
  629. return $this->setTypedValue(
  630. $app,
  631. $key,
  632. json_encode($value, JSON_THROW_ON_ERROR),
  633. $lazy,
  634. self::VALUE_ARRAY | ($sensitive ? self::VALUE_SENSITIVE : 0)
  635. );
  636. } catch (JsonException $e) {
  637. $this->logger->warning('could not setValueArray', ['app' => $app, 'key' => $key, 'exception' => $e]);
  638. throw $e;
  639. }
  640. }
  641. /**
  642. * Store a config key and its value in database
  643. *
  644. * If config key is already known with the exact same config value and same sensitive/lazy status, the
  645. * database is not updated. If config value was previously stored as sensitive, status will not be
  646. * altered.
  647. *
  648. * @param string $app id of the app
  649. * @param string $key config key
  650. * @param string $value config value
  651. * @param bool $lazy config set as lazy loaded
  652. * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
  653. *
  654. * @return bool TRUE if value was updated in database
  655. * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
  656. * @see IAppConfig for explanation about lazy loading
  657. */
  658. private function setTypedValue(
  659. string $app,
  660. string $key,
  661. string $value,
  662. bool $lazy,
  663. int $type
  664. ): bool {
  665. $this->assertParams($app, $key);
  666. $this->loadConfig($lazy);
  667. $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type);
  668. /*
  669. * no update if key is already known with set lazy status, or value is
  670. * different, or sensitivity switched from false to true.
  671. */
  672. if ($this->hasKey($app, $key, $lazy)
  673. && $value === $this->getTypedValue($app, $key, $value, $lazy, $type)
  674. && (!$sensitive || $this->isSensitive($app, $key, $lazy))) {
  675. return false;
  676. }
  677. $refreshCache = false;
  678. $insert = $this->connection->getQueryBuilder();
  679. $insert->insert('appconfig')
  680. ->setValue('appid', $insert->createNamedParameter($app))
  681. ->setValue('lazy', $insert->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
  682. ->setValue('type', $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT))
  683. ->setValue('configkey', $insert->createNamedParameter($key))
  684. ->setValue('configvalue', $insert->createNamedParameter($value));
  685. try {
  686. $insert->executeStatement();
  687. } catch (DBException $e) {
  688. if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
  689. throw $e; // TODO: throw exception or just log and returns false !?
  690. }
  691. $currType = $this->valueTypes[$app][$key] ?? 0;
  692. if ($currType === 0) { // this might happen when switching lazy loading status
  693. $this->loadConfigAll();
  694. $currType = $this->valueTypes[$app][$key] ?? 0;
  695. }
  696. /**
  697. * This should only happen during the upgrade process from 28 to 29.
  698. * We only log a warning and set it to VALUE_MIXED.
  699. */
  700. if ($currType === 0) {
  701. $this->logger->warning('Value type is set to zero (0) in database. This is fine only during the upgrade process from 28 to 29.', ['app' => $app, 'key' => $key]);
  702. $currType = self::VALUE_MIXED;
  703. }
  704. /**
  705. * we only accept a different type from the one stored in database
  706. * if the one stored in database is not-defined (VALUE_MIXED)
  707. */
  708. if (!$this->isTyped(self::VALUE_MIXED, $currType) &&
  709. ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
  710. try {
  711. $currType = $this->convertTypeToString($currType);
  712. $type = $this->convertTypeToString($type);
  713. } catch (AppConfigIncorrectTypeException) {
  714. // can be ignored, this was just needed for a better exception message.
  715. }
  716. throw new AppConfigTypeConflictException('conflict between new type (' . $type . ') and old type (' . $currType . ')');
  717. }
  718. // we fix $type if the stored value, or the new value as it might be changed, is set as sensitive
  719. if ($sensitive || $this->isTyped(self::VALUE_SENSITIVE, $currType)) {
  720. $type = $type | self::VALUE_SENSITIVE;
  721. }
  722. if ($lazy !== $this->isLazy($app, $key)) {
  723. $refreshCache = true;
  724. }
  725. $update = $this->connection->getQueryBuilder();
  726. $update->update('appconfig')
  727. ->set('configvalue', $update->createNamedParameter($value))
  728. ->set('lazy', $update->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT))
  729. ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
  730. ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
  731. ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
  732. $update->executeStatement();
  733. }
  734. if ($refreshCache) {
  735. $this->clearCache();
  736. return true;
  737. }
  738. // update local cache
  739. if ($lazy) {
  740. $cache = &$this->lazyCache;
  741. } else {
  742. $cache = &$this->fastCache;
  743. }
  744. $cache[$app][$key] = $value;
  745. $this->valueTypes[$app][$key] = $type;
  746. return true;
  747. }
  748. /**
  749. * Change the type of config value.
  750. *
  751. * **WARNING:** Method is internal and **MUST** not be used as it may break things.
  752. *
  753. * @param string $app id of the app
  754. * @param string $key config key
  755. * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY}
  756. *
  757. * @return bool TRUE if database update were necessary
  758. * @throws AppConfigUnknownKeyException if $key is now known in database
  759. * @throws AppConfigIncorrectTypeException if $type is not valid
  760. * @internal
  761. * @since 29.0.0
  762. */
  763. public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
  764. $this->assertParams($app, $key);
  765. $this->loadConfigAll();
  766. $lazy = $this->isLazy($app, $key);
  767. if (!$this->hasKey($app, $key, $lazy)) {
  768. throw new AppConfigUnknownKeyException('Unknown config key');
  769. }
  770. // type can only be one type
  771. if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
  772. throw new AppConfigIncorrectTypeException('Unknown value type');
  773. }
  774. $currType = $this->valueTypes[$app][$key];
  775. if (($type | self::VALUE_SENSITIVE) === ($currType | self::VALUE_SENSITIVE)) {
  776. return false;
  777. }
  778. // we complete with sensitive flag if the stored value is set as sensitive
  779. if ($this->isTyped(self::VALUE_SENSITIVE, $currType)) {
  780. $type = $type | self::VALUE_SENSITIVE;
  781. }
  782. $update = $this->connection->getQueryBuilder();
  783. $update->update('appconfig')
  784. ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
  785. ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
  786. ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
  787. $update->executeStatement();
  788. $this->valueTypes[$app][$key] = $type;
  789. return true;
  790. }
  791. /**
  792. * @inheritDoc
  793. *
  794. * @param string $app id of the app
  795. * @param string $key config key
  796. * @param bool $sensitive TRUE to set as sensitive, FALSE to unset
  797. *
  798. * @return bool TRUE if database update were necessary
  799. * @throws AppConfigUnknownKeyException if config key is not known
  800. * @since 29.0.0
  801. */
  802. public function updateSensitive(string $app, string $key, bool $sensitive): bool {
  803. $this->assertParams($app, $key);
  804. $this->loadConfigAll();
  805. if ($sensitive === $this->isSensitive($app, $key, null)) {
  806. return false;
  807. }
  808. /**
  809. * type returned by getValueType() is already cleaned from sensitive flag
  810. * we just need to update it based on $sensitive and store it in database
  811. */
  812. $type = $this->getValueType($app, $key);
  813. if ($sensitive) {
  814. $type = $type | self::VALUE_SENSITIVE;
  815. }
  816. $update = $this->connection->getQueryBuilder();
  817. $update->update('appconfig')
  818. ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT))
  819. ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
  820. ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
  821. $update->executeStatement();
  822. $this->valueTypes[$app][$key] = $type;
  823. return true;
  824. }
  825. /**
  826. * @inheritDoc
  827. *
  828. * @param string $app id of the app
  829. * @param string $key config key
  830. * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset
  831. *
  832. * @return bool TRUE if database update was necessary
  833. * @throws AppConfigUnknownKeyException if config key is not known
  834. * @since 29.0.0
  835. */
  836. public function updateLazy(string $app, string $key, bool $lazy): bool {
  837. $this->assertParams($app, $key);
  838. $this->loadConfigAll();
  839. if ($lazy === $this->isLazy($app, $key)) {
  840. return false;
  841. }
  842. $update = $this->connection->getQueryBuilder();
  843. $update->update('appconfig')
  844. ->set('lazy', $update->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT))
  845. ->where($update->expr()->eq('appid', $update->createNamedParameter($app)))
  846. ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key)));
  847. $update->executeStatement();
  848. // At this point, it is a lot safer to clean cache
  849. $this->clearCache();
  850. return true;
  851. }
  852. /**
  853. * @inheritDoc
  854. *
  855. * @param string $app id of the app
  856. * @param string $key config key
  857. *
  858. * @return array
  859. * @throws AppConfigUnknownKeyException if config key is not known in database
  860. * @since 29.0.0
  861. */
  862. public function getDetails(string $app, string $key): array {
  863. $this->assertParams($app, $key);
  864. $this->loadConfigAll();
  865. $lazy = $this->isLazy($app, $key);
  866. if ($lazy) {
  867. $cache = $this->lazyCache;
  868. } else {
  869. $cache = $this->fastCache;
  870. }
  871. $type = $this->getValueType($app, $key);
  872. try {
  873. $typeString = $this->convertTypeToString($type);
  874. } catch (AppConfigIncorrectTypeException $e) {
  875. $this->logger->warning('type stored in database is not correct', ['exception' => $e, 'type' => $type]);
  876. $typeString = (string)$type;
  877. }
  878. if (!isset($cache[$app][$key])) {
  879. throw new AppConfigUnknownKeyException('unknown config key');
  880. }
  881. return [
  882. 'app' => $app,
  883. 'key' => $key,
  884. 'value' => $cache[$app][$key],
  885. 'type' => $type,
  886. 'lazy' => $lazy,
  887. 'typeString' => $typeString,
  888. 'sensitive' => $this->isSensitive($app, $key, null)
  889. ];
  890. }
  891. /**
  892. * @param string $type
  893. *
  894. * @return int
  895. * @throws AppConfigIncorrectTypeException
  896. * @since 29.0.0
  897. */
  898. public function convertTypeToInt(string $type): int {
  899. return match (strtolower($type)) {
  900. 'mixed' => IAppConfig::VALUE_MIXED,
  901. 'string' => IAppConfig::VALUE_STRING,
  902. 'integer' => IAppConfig::VALUE_INT,
  903. 'float' => IAppConfig::VALUE_FLOAT,
  904. 'boolean' => IAppConfig::VALUE_BOOL,
  905. 'array' => IAppConfig::VALUE_ARRAY,
  906. default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type)
  907. };
  908. }
  909. /**
  910. * @param int $type
  911. *
  912. * @return string
  913. * @throws AppConfigIncorrectTypeException
  914. * @since 29.0.0
  915. */
  916. public function convertTypeToString(int $type): string {
  917. $type &= ~self::VALUE_SENSITIVE;
  918. return match ($type) {
  919. IAppConfig::VALUE_MIXED => 'mixed',
  920. IAppConfig::VALUE_STRING => 'string',
  921. IAppConfig::VALUE_INT => 'integer',
  922. IAppConfig::VALUE_FLOAT => 'float',
  923. IAppConfig::VALUE_BOOL => 'boolean',
  924. IAppConfig::VALUE_ARRAY => 'array',
  925. default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type)
  926. };
  927. }
  928. /**
  929. * @inheritDoc
  930. *
  931. * @param string $app id of the app
  932. * @param string $key config key
  933. *
  934. * @since 29.0.0
  935. */
  936. public function deleteKey(string $app, string $key): void {
  937. $this->assertParams($app, $key);
  938. $qb = $this->connection->getQueryBuilder();
  939. $qb->delete('appconfig')
  940. ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
  941. ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key)));
  942. $qb->executeStatement();
  943. unset($this->lazyCache[$app][$key]);
  944. unset($this->fastCache[$app][$key]);
  945. }
  946. /**
  947. * @inheritDoc
  948. *
  949. * @param string $app id of the app
  950. *
  951. * @since 29.0.0
  952. */
  953. public function deleteApp(string $app): void {
  954. $this->assertParams($app);
  955. $qb = $this->connection->getQueryBuilder();
  956. $qb->delete('appconfig')
  957. ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)));
  958. $qb->executeStatement();
  959. $this->clearCache();
  960. }
  961. /**
  962. * @inheritDoc
  963. *
  964. * @param bool $reload set to TRUE to refill cache instantly after clearing it
  965. *
  966. * @since 29.0.0
  967. */
  968. public function clearCache(bool $reload = false): void {
  969. $this->lazyLoaded = $this->fastLoaded = false;
  970. $this->lazyCache = $this->fastCache = $this->valueTypes = [];
  971. if (!$reload) {
  972. return;
  973. }
  974. $this->loadConfigAll();
  975. }
  976. /**
  977. * For debug purpose.
  978. * Returns the cached data.
  979. *
  980. * @return array
  981. * @since 29.0.0
  982. * @internal
  983. */
  984. public function statusCache(): array {
  985. return [
  986. 'fastLoaded' => $this->fastLoaded,
  987. 'fastCache' => $this->fastCache,
  988. 'lazyLoaded' => $this->lazyLoaded,
  989. 'lazyCache' => $this->lazyCache,
  990. ];
  991. }
  992. /**
  993. * @param int $needle bitflag to search
  994. * @param int $type known value
  995. *
  996. * @return bool TRUE if bitflag $needle is set in $type
  997. */
  998. private function isTyped(int $needle, int $type): bool {
  999. return (($needle & $type) !== 0);
  1000. }
  1001. /**
  1002. * Confirm the string set for app and key fit the database description
  1003. *
  1004. * @param string $app assert $app fit in database
  1005. * @param string $configKey assert config key fit in database
  1006. * @param bool $allowEmptyApp $app can be empty string
  1007. * @param int $valueType assert value type is only one type
  1008. *
  1009. * @throws InvalidArgumentException
  1010. */
  1011. private function assertParams(string $app = '', string $configKey = '', bool $allowEmptyApp = false, int $valueType = -1): void {
  1012. if (!$allowEmptyApp && $app === '') {
  1013. throw new InvalidArgumentException('app cannot be an empty string');
  1014. }
  1015. if (strlen($app) > self::APP_MAX_LENGTH) {
  1016. throw new InvalidArgumentException(
  1017. 'Value (' . $app . ') for app is too long (' . self::APP_MAX_LENGTH . ')'
  1018. );
  1019. }
  1020. if (strlen($configKey) > self::KEY_MAX_LENGTH) {
  1021. throw new InvalidArgumentException('Value (' . $configKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')');
  1022. }
  1023. if ($valueType > -1) {
  1024. $valueType &= ~self::VALUE_SENSITIVE;
  1025. if (!in_array($valueType, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
  1026. throw new InvalidArgumentException('Unknown value type');
  1027. }
  1028. }
  1029. }
  1030. private function loadConfigAll(): void {
  1031. $this->loadConfig(null);
  1032. }
  1033. /**
  1034. * Load normal config or config set as lazy loaded
  1035. *
  1036. * @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config
  1037. */
  1038. private function loadConfig(?bool $lazy = false): void {
  1039. if ($this->isLoaded($lazy)) {
  1040. return;
  1041. }
  1042. $qb = $this->connection->getQueryBuilder();
  1043. $qb->from('appconfig');
  1044. /**
  1045. * The use of $this->>migrationCompleted is only needed to manage the
  1046. * database during the upgrading process to nc29.
  1047. */
  1048. if (!$this->migrationCompleted) {
  1049. $qb->select('appid', 'configkey', 'configvalue');
  1050. } else {
  1051. // we only need value from lazy when loadConfig does not specify it
  1052. $qb->select('appid', 'configkey', 'configvalue', 'type');
  1053. if ($lazy !== null) {
  1054. $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT)));
  1055. } else {
  1056. $qb->addSelect('lazy');
  1057. }
  1058. }
  1059. try {
  1060. $result = $qb->executeQuery();
  1061. } catch (DBException $e) {
  1062. /**
  1063. * in case of issue with field name, it means that migration is not completed.
  1064. * Falling back to a request without select on lazy.
  1065. * This whole try/catch and the migrationCompleted variable can be removed in NC30.
  1066. */
  1067. if ($e->getReason() !== DBException::REASON_INVALID_FIELD_NAME) {
  1068. throw $e;
  1069. }
  1070. $this->migrationCompleted = false;
  1071. $this->loadConfig($lazy);
  1072. return;
  1073. }
  1074. $rows = $result->fetchAll();
  1075. foreach ($rows as $row) {
  1076. // most of the time, 'lazy' is not in the select because its value is already known
  1077. if (($row['lazy'] ?? ($lazy ?? 0) ? 1 : 0) === 1) {
  1078. $cache = &$this->lazyCache;
  1079. } else {
  1080. $cache = &$this->fastCache;
  1081. }
  1082. $cache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? '';
  1083. $this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0);
  1084. }
  1085. $result->closeCursor();
  1086. $this->setAsLoaded($lazy);
  1087. }
  1088. /**
  1089. * if $lazy is:
  1090. * - false: will returns true if fast config is loaded
  1091. * - true : will returns true if lazy config is loaded
  1092. * - null : will returns true if both config are loaded
  1093. *
  1094. * @param bool $lazy
  1095. *
  1096. * @return bool
  1097. */
  1098. private function isLoaded(?bool $lazy): bool {
  1099. if ($lazy === null) {
  1100. return $this->lazyLoaded && $this->fastLoaded;
  1101. }
  1102. return $lazy ? $this->lazyLoaded : $this->fastLoaded;
  1103. }
  1104. /**
  1105. * if $lazy is:
  1106. * - false: set fast config as loaded
  1107. * - true : set lazy config as loaded
  1108. * - null : set both config as loaded
  1109. *
  1110. * @param bool $lazy
  1111. */
  1112. private function setAsLoaded(?bool $lazy): void {
  1113. if ($lazy === null) {
  1114. $this->fastLoaded = true;
  1115. $this->lazyLoaded = true;
  1116. return;
  1117. }
  1118. if ($lazy) {
  1119. $this->lazyLoaded = true;
  1120. } else {
  1121. $this->fastLoaded = true;
  1122. }
  1123. }
  1124. /**
  1125. * Gets the config value
  1126. *
  1127. * @param string $app app
  1128. * @param string $key key
  1129. * @param string $default = null, default value if the key does not exist
  1130. *
  1131. * @return string the value or $default
  1132. * @deprecated - use getValue*()
  1133. *
  1134. * This function gets a value from the appconfig table. If the key does
  1135. * not exist the default value will be returned
  1136. */
  1137. public function getValue($app, $key, $default = null) {
  1138. $this->loadConfig();
  1139. return $this->fastCache[$app][$key] ?? $default;
  1140. }
  1141. /**
  1142. * Sets a value. If the key did not exist before it will be created.
  1143. *
  1144. * @param string $app app
  1145. * @param string $key key
  1146. * @param string|float|int $value value
  1147. *
  1148. * @return bool True if the value was inserted or updated, false if the value was the same
  1149. * @throws AppConfigTypeConflictException
  1150. * @throws AppConfigUnknownKeyException
  1151. * @deprecated
  1152. */
  1153. public function setValue($app, $key, $value) {
  1154. /**
  1155. * TODO: would it be overkill, or decently improve performance, to catch
  1156. * call to this method with $key='enabled' and 'hide' config value related
  1157. * to $app when the app is disabled (by modifying entry in database: lazy=lazy+2)
  1158. * or enabled (lazy=lazy-2)
  1159. *
  1160. * this solution would remove the loading of config values from disabled app
  1161. * unless calling the method {@see loadConfigAll()}
  1162. */
  1163. return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED);
  1164. }
  1165. /**
  1166. * get multiple values, either the app or key can be used as wildcard by setting it to false
  1167. *
  1168. * @param string|false $app
  1169. * @param string|false $key
  1170. *
  1171. * @return array|false
  1172. * @deprecated 29.0.0 use getAllValues()
  1173. */
  1174. public function getValues($app, $key) {
  1175. if (($app !== false) === ($key !== false)) {
  1176. return false;
  1177. }
  1178. $key = ($key === false) ? '' : $key;
  1179. if (!$app) {
  1180. return $this->searchValues($key);
  1181. } else {
  1182. return $this->getAllValues($app, $key);
  1183. }
  1184. }
  1185. /**
  1186. * get all values of the app or and filters out sensitive data
  1187. *
  1188. * @param string $app
  1189. *
  1190. * @return array
  1191. * @deprecated 29.0.0 use getAllValues()
  1192. */
  1193. public function getFilteredValues($app) {
  1194. return $this->getAllValues($app, filtered: true);
  1195. }
  1196. /**
  1197. * @param string $app
  1198. *
  1199. * @return string[]
  1200. * @deprecated data sensitivity should be set when calling setValue*()
  1201. */
  1202. private function getSensitiveKeys(string $app): array {
  1203. $sensitiveValues = [
  1204. 'circles' => [
  1205. '/^key_pairs$/',
  1206. '/^local_gskey$/',
  1207. ],
  1208. 'external' => [
  1209. '/^sites$/',
  1210. ],
  1211. 'integration_discourse' => [
  1212. '/^private_key$/',
  1213. '/^public_key$/',
  1214. ],
  1215. 'integration_dropbox' => [
  1216. '/^client_id$/',
  1217. '/^client_secret$/',
  1218. ],
  1219. 'integration_github' => [
  1220. '/^client_id$/',
  1221. '/^client_secret$/',
  1222. ],
  1223. 'integration_gitlab' => [
  1224. '/^client_id$/',
  1225. '/^client_secret$/',
  1226. '/^oauth_instance_url$/',
  1227. ],
  1228. 'integration_google' => [
  1229. '/^client_id$/',
  1230. '/^client_secret$/',
  1231. ],
  1232. 'integration_jira' => [
  1233. '/^client_id$/',
  1234. '/^client_secret$/',
  1235. '/^forced_instance_url$/',
  1236. ],
  1237. 'integration_onedrive' => [
  1238. '/^client_id$/',
  1239. '/^client_secret$/',
  1240. ],
  1241. 'integration_openproject' => [
  1242. '/^client_id$/',
  1243. '/^client_secret$/',
  1244. '/^oauth_instance_url$/',
  1245. ],
  1246. 'integration_reddit' => [
  1247. '/^client_id$/',
  1248. '/^client_secret$/',
  1249. ],
  1250. 'integration_suitecrm' => [
  1251. '/^client_id$/',
  1252. '/^client_secret$/',
  1253. '/^oauth_instance_url$/',
  1254. ],
  1255. 'integration_twitter' => [
  1256. '/^consumer_key$/',
  1257. '/^consumer_secret$/',
  1258. '/^followed_user$/',
  1259. ],
  1260. 'integration_zammad' => [
  1261. '/^client_id$/',
  1262. '/^client_secret$/',
  1263. '/^oauth_instance_url$/',
  1264. ],
  1265. 'notify_push' => [
  1266. '/^cookie$/',
  1267. ],
  1268. 'serverinfo' => [
  1269. '/^token$/',
  1270. ],
  1271. 'spreed' => [
  1272. '/^bridge_bot_password$/',
  1273. '/^hosted-signaling-server-(.*)$/',
  1274. '/^recording_servers$/',
  1275. '/^signaling_servers$/',
  1276. '/^signaling_ticket_secret$/',
  1277. '/^signaling_token_privkey_(.*)$/',
  1278. '/^signaling_token_pubkey_(.*)$/',
  1279. '/^sip_bridge_dialin_info$/',
  1280. '/^sip_bridge_shared_secret$/',
  1281. '/^stun_servers$/',
  1282. '/^turn_servers$/',
  1283. '/^turn_server_secret$/',
  1284. ],
  1285. 'support' => [
  1286. '/^last_response$/',
  1287. '/^potential_subscription_key$/',
  1288. '/^subscription_key$/',
  1289. ],
  1290. 'theming' => [
  1291. '/^imprintUrl$/',
  1292. '/^privacyUrl$/',
  1293. '/^slogan$/',
  1294. '/^url$/',
  1295. ],
  1296. 'user_ldap' => [
  1297. '/^(s..)?ldap_agent_password$/',
  1298. ],
  1299. 'user_saml' => [
  1300. '/^idp-x509cert$/',
  1301. ],
  1302. ];
  1303. return $sensitiveValues[$app] ?? [];
  1304. }
  1305. /**
  1306. * Clear all the cached app config values
  1307. * New cache will be generated next time a config value is retrieved
  1308. *
  1309. * @deprecated use {@see clearCache()}
  1310. */
  1311. public function clearCachedConfig(): void {
  1312. $this->clearCache();
  1313. }
  1314. }