AppConfig.php 41 KB

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