From 2a62eb82befcfdc9600e463f1812690d3fcba60b Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 9 Sep 2021 12:32:16 +0100 Subject: [PATCH 1/4] Add new redis key for email verification --- packages/auth/src/redis/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/auth/src/redis/utils.js b/packages/auth/src/redis/utils.js index 415dcbf463..17ecc51bc5 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", From d919c4418562bc6020605486d669e9b3a3f4788e Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Thu, 9 Sep 2021 17:08:27 +0100 Subject: [PATCH 2/4] Add pre-hased password option to admin creation --- .../worker/src/api/controllers/global/users.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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) } From fb01a66936446f316393c15f78bd5f7804ebe1d6 Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 13 Sep 2021 17:38:12 +0100 Subject: [PATCH 3/4] Configurable user cache population in auth middleware --- packages/auth/src/cache/user.js | 24 +++++++++++++++++-- packages/auth/src/middleware/authenticated.js | 15 ++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index e0936182a7..7d0cf698eb 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 {*} loadUser 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) { From ba199a592ce8ba9c8ae4d8dd2075d1d2de23862f Mon Sep 17 00:00:00 2001 From: Rory Powell Date: Mon, 13 Sep 2021 17:45:37 +0100 Subject: [PATCH 4/4] Fix typo --- packages/auth/src/cache/user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/auth/src/cache/user.js b/packages/auth/src/cache/user.js index 7d0cf698eb..669192a905 100644 --- a/packages/auth/src/cache/user.js +++ b/packages/auth/src/cache/user.js @@ -16,7 +16,7 @@ const populateFromDB = (userId, tenantId) => { * 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 {*} loadUser function to provide the user for re-caching. default to couch db + * @param {*} populateUser function to provide the user for re-caching. default to couch db * @returns */ exports.getUser = async (