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

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