diff --git a/src/settings.js b/src/settings.js index ef79ad6a7..a75459abe 100644 --- a/src/settings.js +++ b/src/settings.js @@ -59,6 +59,9 @@ exports = module.exports = { getFirewallBlocklist, setFirewallBlocklist, + getGhosts, + setGhosts, + getSupportConfig, provider, list, @@ -93,6 +96,7 @@ exports = module.exports = { APPSTORE_LISTING_CONFIG_KEY: 'appstore_listing_config', SUPPORT_CONFIG_KEY: 'support_config', DIRECTORY_CONFIG_KEY: 'directory_config', + GHOSTS_CONFIG_KEY: 'ghosts_config', // strings AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern', @@ -197,6 +201,8 @@ const gDefaults = (function () { whitelist: null // null imples nothing is whitelisted. this is an array }; + result[exports.GHOSTS_CONFIG_KEY] = {}; + result[exports.SUPPORT_CONFIG_KEY] = { email: 'support@cloudron.io', remoteSupport: true, @@ -579,6 +585,20 @@ async function setFirewallBlocklist(blocklist) { await setBlob(exports.FIREWALL_BLOCKLIST_KEY, Buffer.from(blocklist)); } +async function getGhosts() { + const value = await get(exports.GHOSTS_CONFIG_KEY); + if (value === null) return gDefaults[exports.GHOSTS_CONFIG_KEY]; + + return JSON.parse(value); +} + +async function setGhosts(ghosts) { + assert.strictEqual(typeof ghosts, 'object'); + + await set(exports.GHOSTS_CONFIG_KEY, JSON.stringify(ghosts)); + notifyChange(exports.GHOSTS_CONFIG_KEY, ghosts); +} + async function getSupportConfig() { const value = await get(exports.SUPPORT_CONFIG_KEY); if (value === null) return gDefaults[exports.SUPPORT_CONFIG_KEY]; diff --git a/src/users.js b/src/users.js index e29b65b13..02c5d949c 100644 --- a/src/users.js +++ b/src/users.js @@ -254,21 +254,22 @@ async function setGhost(user, password, expiresAt) { debug(`setGhost: ${user.username} expiresAt ${expiresAt}`); - let ghostData = safe.JSON.parse(safe.fs.readFileSync(paths.GHOST_USER_FILE, 'utf8')); - if (!ghostData) ghostData = {}; + const [errorGet, ghostData] = await safe(settings.getGhosts()); + if (errorGet) throw errorGet; ghostData[user.username] = { password, expiresAt }; - if (!safe.fs.writeFileSync(paths.GHOST_USER_FILE, JSON.stringify(ghostData, null, 4), 'utf8')) throw new BoxError(BoxError.FS_ERROR); + const [errorSet] = await safe(settings.setGhosts(ghostData)); + if (errorSet) throw errorSet; } // returns true if ghost user was matched -function verifyGhost(username, password) { +async function verifyGhost(username, password) { assert.strictEqual(typeof username, 'string'); assert.strictEqual(typeof password, 'string'); - var ghostData = safe.JSON.parse(safe.fs.readFileSync(paths.GHOST_USER_FILE, 'utf8')); - if (!ghostData) return false; + const [error, ghostData] = await safe(settings.getGhosts()); + if (error) throw error; // 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) { @@ -276,7 +277,10 @@ function verifyGhost(username, password) { if (ghostData[username].expiresAt < Date.now()) { debug('verifyGhost: password expired'); delete ghostData[username]; - safe.fs.writeFileSync(paths.GHOST_USER_FILE, JSON.stringify(ghostData, null, 4), 'utf8'); + + const [error] = await safe(settings.setGhosts(ghostData)); + if (error) throw error; + return false; } else if (ghostData[username].password === password) { debug('verifyGhost: matched ghost user'); @@ -287,7 +291,10 @@ function verifyGhost(username, password) { } else if(ghostData[username] === password) { debug('verifyGhost: matched ghost user'); delete ghostData[username]; - safe.fs.writeFileSync(paths.GHOST_USER_FILE, JSON.stringify(ghostData, null, 4), 'utf8'); + + const [error] = await safe(settings.setGhosts(ghostData)); + if (error) throw error; + return true; } } @@ -320,9 +327,14 @@ async function verify(userId, password, identifier) { if (!user.active) throw new BoxError(BoxError.NOT_FOUND, 'User not active'); // for just invited users the username may be still null - if (user.username && verifyGhost(user.username, password)) { - user.ghost = true; - return user; + if (user.username) { + const [error, valid] = await safe(verifyGhost(user.username, password)); + if (error) console.error('Failed to verify ghost.', error); + + if (valid) { + user.ghost = true; + return user; + } } const [error] = await safe(verifyAppPassword(user.id, password, identifier));