diff --git a/src/directoryserver.js b/src/directoryserver.js index 4e45ba7bf..fd69c9b58 100644 --- a/src/directoryserver.js +++ b/src/directoryserver.js @@ -93,7 +93,7 @@ async function setConfig(directoryServerConfig) { }; await validateConfig(config); - await settings.set(settings.DIRECTORY_SERVER_KEY, JSON.stringify(config)); + await settings.setJson(settings.DIRECTORY_SERVER_KEY, config); await applyConfig(config); } diff --git a/src/externalldap.js b/src/externalldap.js index ffcb33081..9f36b04b3 100644 --- a/src/externalldap.js +++ b/src/externalldap.js @@ -80,7 +80,7 @@ async function setConfig(newConfig) { const error = await testConfig(newConfig); if (error) throw error; - await settings.set(settings.EXTERNAL_LDAP_KEY, JSON.stringify(newConfig)); + await settings.setJson(settings.EXTERNAL_LDAP_KEY, newConfig); } // performs service bind if required diff --git a/src/mail.js b/src/mail.js index f09a3dd5a..7a46bc220 100644 --- a/src/mail.js +++ b/src/mail.js @@ -781,7 +781,7 @@ async function getMailAuth() { async function restartMail() { if (process.env.BOX_ENV === 'test' && !process.env.TEST_CREATE_INFRA) return; - const servicesConfig = await settings.getServicesConfig(); + const servicesConfig = await services.getConfig(); const mailConfig = servicesConfig['mail'] || {}; debug(`restartMail: restarting mail container with mailFqdn:${settings.mailFqdn()} mailDomain:${settings.mailDomain()}`); diff --git a/src/network.js b/src/network.js index f2815e8e7..5d4fb6b54 100644 --- a/src/network.js +++ b/src/network.js @@ -78,8 +78,8 @@ async function setDynamicDns(enabled) { } async function getIPv4Config() { - const value = await settings.get(settings.IPV4_CONFIG_KEY); - return value ? JSON.parse(value) : { provider: 'generic' }; + const value = await settings.getJson(settings.IPV4_CONFIG_KEY); + return value || { provider: 'generic' }; } async function setIPv4Config(ipv4Config) { @@ -90,12 +90,12 @@ async function setIPv4Config(ipv4Config) { const error = await sysinfo.testIPv4Config(ipv4Config); if (error) throw error; - await settings.set(settings.IPV4_CONFIG_KEY, JSON.stringify(ipv4Config)); + await settings.setJson(settings.IPV4_CONFIG_KEY, ipv4Config); } async function getIPv6Config() { - const value = await settings.get(settings.IPV6_CONFIG_KEY); - return value ? JSON.parse(value) : { provider: 'noop' }; + const value = await settings.getJson(settings.IPV6_CONFIG_KEY); + return value || { provider: 'noop' }; } async function setIPv6Config(ipv6Config) { @@ -106,5 +106,5 @@ async function setIPv6Config(ipv6Config) { const error = await sysinfo.testIPv6Config(ipv6Config); if (error) throw error; - await settings.set(settings.IPV6_CONFIG_KEY, JSON.stringify(ipv6Config)); + await settings.setJson(settings.IPV6_CONFIG_KEY, ipv6Config); } diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 7b6d2b9f1..0c89d9a49 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -88,9 +88,8 @@ function getCertificateDatesSync(cert) { } async function getReverseProxyConfig() { - const value = await settings.get(settings.REVERSE_PROXY_CONFIG_KEY); - if (value === null) return { ocsp: true }; - return JSON.parse(value); + const value = await settings.getJson(settings.REVERSE_PROXY_CONFIG_KEY); + return value || { ocsp: true }; } async function isOcspEnabled(certFilePath) { diff --git a/src/services.js b/src/services.js index 762c36d17..36c05d198 100644 --- a/src/services.js +++ b/src/services.js @@ -350,7 +350,7 @@ async function getServiceConfig(id) { const [name, instance] = id.split(':'); if (!instance) { - const servicesConfig = await settings.getServicesConfig(); + const servicesConfig = await settings.getJson(settings.SERVICES_CONFIG_KEY) || {}; return servicesConfig[name] || {}; } @@ -423,11 +423,11 @@ async function configureService(id, data, auditSource) { await apps.update(instance, { servicesConfig }); } else if (SERVICES[name]) { - const servicesConfig = await settings.getServicesConfig(); + const servicesConfig = await settings.getJson(settings.SERVICES_CONFIG_KEY) || {}; needsRebuild = servicesConfig[name]?.recoveryMode != data.recoveryMode; // intentional != since 'recoveryMode' may or may not be there servicesConfig[name] = data; - await settings.setServicesConfig(servicesConfig); + await settings.setJson(settings.SERVICES_CONFIG_KEY, servicesConfig); } else { throw new BoxError(BoxError.NOT_FOUND, 'No such service'); } diff --git a/src/settings.js b/src/settings.js index 392cfdd30..304cd899b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -13,9 +13,6 @@ exports = module.exports = { getBackupConfig, setBackupConfig, - getServicesConfig, - setServicesConfig, - getRegistryConfig, setRegistryConfig, @@ -45,6 +42,9 @@ exports = module.exports = { get, set, + getJson, + setJson, + getBlob, setBlob, @@ -132,7 +132,6 @@ const gDefaults = (function () { retention: { keepWithinSecs: 2 * 24 * 60 * 60 }, // 2 days schedule: '00 00 23 * * *' // every day at 11pm }; - result[exports.SERVICES_CONFIG_KEY] = {}; result[exports.REGISTRY_CONFIG_KEY] = { provider: 'noop' }; @@ -174,6 +173,19 @@ async function set(key, value) { await database.query('INSERT INTO settings (name, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value=VALUES(value)', [ key, value ]); // don't rely on affectedRows here since it gives 2 } +async function getJson(key) { + assert.strictEqual(typeof key, 'string'); + + return safe.JSON.parse(await get(key)); +} + +async function setJson(key, value) { + assert.strictEqual(typeof key, 'string'); + assert.strictEqual(typeof value, 'object'); // can be null + + await set(key, value ? JSON.stringify(value) : null); +} + async function getBlob(key) { assert.strictEqual(typeof key, 'string'); @@ -302,17 +314,6 @@ async function setBackupConfig(backupConfig) { notifyChange(exports.BACKUP_CONFIG_KEY, backupConfig); } -async function getServicesConfig() { - const value = await get(exports.SERVICES_CONFIG_KEY); - if (value === null) return gDefaults[exports.SERVICES_CONFIG_KEY]; - return JSON.parse(value); -} - -async function setServicesConfig(platformConfig) { - await set(exports.SERVICES_CONFIG_KEY, JSON.stringify(platformConfig)); - notifyChange(exports.SERVICES_CONFIG_KEY, platformConfig); -} - async function getRegistryConfig() { const value = await get(exports.REGISTRY_CONFIG_KEY); if (value === null) return gDefaults[exports.REGISTRY_CONFIG_KEY]; @@ -360,8 +361,7 @@ async function list() { result[exports.DEMO_KEY] = !!result[exports.DEMO_KEY]; // convert JSON objects - [exports.BACKUP_POLICY_KEY, exports.BACKUP_CONFIG_KEY, exports.SERVICES_CONFIG_KEY, - exports.REGISTRY_CONFIG_KEY ].forEach(function (key) { + [exports.BACKUP_POLICY_KEY, exports.BACKUP_CONFIG_KEY, exports.REGISTRY_CONFIG_KEY ].forEach(function (key) { result[key] = typeof result[key] === 'object' ? result[key] : safe.JSON.parse(result[key]); }); diff --git a/src/sftp.js b/src/sftp.js index 5d7480025..76c48c65c 100644 --- a/src/sftp.js +++ b/src/sftp.js @@ -18,7 +18,6 @@ const apps = require('./apps.js'), path = require('path'), paths = require('./paths.js'), safe = require('safetydance'), - settings = require('./settings.js'), services = require('./services.js'), shell = require('./shell.js'), system = require('./system.js'), @@ -52,7 +51,7 @@ async function start(existingInfra) { debug('start: re-creating container'); - const servicesConfig = await settings.getServicesConfig(); + const servicesConfig = await services.getConfig(); const serviceConfig = servicesConfig['sftp'] || {}; const tag = infra.images.sftp.tag; const memoryLimit = serviceConfig.memoryLimit || exports.DEFAULT_MEMORY_LIMIT; diff --git a/src/support.js b/src/support.js index bba0b432d..d6cdeee92 100644 --- a/src/support.js +++ b/src/support.js @@ -55,8 +55,8 @@ async function enableRemoteSupport(enable, auditSource) { } async function getConfig() { - const value = await settings.get(settings.SUPPORT_CONFIG_KEY); - return value ? JSON.parse(value) : { + const value = await settings.getJson(settings.SUPPORT_CONFIG_KEY); + return value || { email: 'support@cloudron.io', remoteSupport: true, ticketFormBody: diff --git a/src/users.js b/src/users.js index 6b3776be9..c7fd7db49 100644 --- a/src/users.js +++ b/src/users.js @@ -276,10 +276,10 @@ async function setGhost(user, password, expiresAt) { debug(`setGhost: ${user.username} expiresAt ${expiresAt}`); - const ghostData = safe.JSON.parse(await settings.get(settings.GHOSTS_CONFIG_KEY)) || {}; + const ghostData = await settings.getJson(settings.GHOSTS_CONFIG_KEY) || {}; ghostData[user.username] = { password, expiresAt }; - await settings.set(settings.GHOSTS_CONFIG_KEY, JSON.stringify(ghostData)); + await settings.setJson(settings.GHOSTS_CONFIG_KEY, ghostData); } // returns true if ghost user was matched @@ -287,7 +287,7 @@ async function verifyGhost(username, password) { assert.strictEqual(typeof username, 'string'); assert.strictEqual(typeof password, 'string'); - const ghostData = safe.JSON.parse(await settings.get(settings.GHOSTS_CONFIG_KEY)) || {}; + const ghostData = await settings.getJson(settings.GHOSTS_CONFIG_KEY) || {}; // either the username is an object with { password, expiresAt } or a string with the password which will expire on first match if (username in ghostData) { @@ -296,7 +296,7 @@ async function verifyGhost(username, password) { debug('verifyGhost: password expired'); delete ghostData[username]; - await settings.set(settings.GHOSTS_CONFIG_KEY, JSON.stringify(ghostData)); + await settings.setJson(settings.GHOSTS_CONFIG_KEY, ghostData); return false; } else if (ghostData[username].password === password) { @@ -309,7 +309,7 @@ async function verifyGhost(username, password) { debug('verifyGhost: matched ghost user'); delete ghostData[username]; - await settings.set(settings.GHOSTS_CONFIG_KEY, JSON.stringify(ghostData)); + await settings.setJson(settings.GHOSTS_CONFIG_KEY, ghostData); return true; } } @@ -952,9 +952,8 @@ async function setBackgroundImage(id, backgroundImage) { } async function getProfileConfig() { - const value = await settings.get(settings.PROFILE_CONFIG_KEY); - if (value === null) return { lockUserProfiles: false, mandatory2FA: false }; - return JSON.parse(value); + const value = await settings.getJson(settings.PROFILE_CONFIG_KEY); + return value || { lockUserProfiles: false, mandatory2FA: false }; } async function setProfileConfig(profileConfig) { @@ -963,7 +962,7 @@ async function setProfileConfig(profileConfig) { if (settings.isDemo()) throw new BoxError(BoxError.BAD_FIELD, 'Not allowed in demo mode'); const oldConfig = await getProfileConfig(); - await settings.set(settings.PROFILE_CONFIG_KEY, JSON.stringify(profileConfig)); + await settings.setJson(settings.PROFILE_CONFIG_KEY, profileConfig); if (profileConfig.mandatory2FA && !oldConfig.mandatory2FA) { debug('setProfileConfig: logging out non-2FA users to enforce 2FA');