diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index e0936182a7..669192a905 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -3,7 +3,27 @@ const { getTenantId, lookupTenantId, getGlobalDB } = require("../tenancy") const EXPIRY_SECONDS = 3600 -exports.getUser = async (userId, tenantId = null) => { +/** + * The default populate user function + */ +const populateFromDB = (userId, tenantId) => { + return getGlobalDB(tenantId).get(userId) +} + +/** + * Get the requested user by id. + * Use redis cache to first read the user. + * If not present fallback to loading the user directly and re-caching. + * @param {*} userId the id of the user to get + * @param {*} tenantId the tenant of the user to get + * @param {*} populateUser function to provide the user for re-caching. default to couch db + * @returns + */ +exports.getUser = async ( + userId, + tenantId = null, + populateUser = populateFromDB +) => { if (!tenantId) { try { tenantId = getTenantId() @@ -15,7 +35,7 @@ exports.getUser = async (userId, tenantId = null) => { // try cache let user = await client.get(userId) if (!user) { - user = await getGlobalDB(tenantId).get(userId) + user = await populateUser(userId, tenantId) client.store(userId, user, EXPIRY_SECONDS) } if (user && !user.tenantId && tenantId) { diff --git a/packages/auth/src/middleware/authenticated.js b/packages/auth/src/middleware/authenticated.js index e3705a9a24..944f3ee9d9 100644 --- a/packages/auth/src/middleware/authenticated.js +++ b/packages/auth/src/middleware/authenticated.js @@ -21,7 +21,10 @@ function finalise( * The tenancy modules should not be used here and it should be assumed that the tenancy context * has not yet been populated. */ -module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { +module.exports = ( + noAuthPatterns = [], + opts = { publicAllowed: false, populateUser: null } +) => { const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] return async (ctx, next) => { let publicEndpoint = false @@ -46,7 +49,15 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { error = "No session found" } else { try { - user = await getUser(userId, session.tenantId) + if (opts && opts.populateUser) { + user = await getUser( + userId, + session.tenantId, + opts.populateUser(ctx) + ) + } else { + user = await getUser(userId, session.tenantId) + } delete user.password authenticated = true } catch (err) { diff --git a/packages/auth/src/redis/utils.js b/packages/auth/src/redis/utils.js index 09b4905298..cd4bfdc6e2 100644 --- a/packages/auth/src/redis/utils.js +++ b/packages/auth/src/redis/utils.js @@ -8,6 +8,7 @@ const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD exports.Databases = { PW_RESETS: "pwReset", + VERIFICATIONS: "verification", INVITATIONS: "invitation", DEV_LOCKS: "devLocks", DEBOUNCE: "debounce", diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index 24b00fe3a6..7fd367964c 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -33,7 +33,7 @@ async function allUsers() { return response.rows.map(row => row.doc) } -async function saveUser(user, tenantId) { +async function saveUser(user, tenantId, hashPassword = true) { if (!tenantId) { throw "No tenancy specified." } @@ -56,7 +56,7 @@ async function saveUser(user, tenantId) { // get the password, make sure one is defined let hashedPassword if (password) { - hashedPassword = await hash(password) + hashedPassword = hashPassword ? await hash(password) : password } else if (dbUser) { hashedPassword = dbUser.password } else { @@ -110,6 +110,15 @@ exports.save = async ctx => { exports.adminUser = async ctx => { const { email, password, tenantId } = ctx.request.body + + // account portal sends a pre-hashed password - honour param to prevent double hashing + let hashPassword = ctx.request.query.hashPassword + if (hashPassword && hashPassword == "false") { + hashPassword = false + } else { + hashPassword = true + } + if (await doesTenantExist(tenantId)) { ctx.throw(403, "Organisation already exists.") } @@ -141,7 +150,7 @@ exports.adminUser = async ctx => { tenantId, } try { - ctx.body = await saveUser(user, tenantId) + ctx.body = await saveUser(user, tenantId, hashPassword) } catch (err) { ctx.throw(err.status || 400, err) }