Merge pull request #2574 from Budibase/feature/onboarding-backend

Add new redis key for email verification & pre-hashed password option
This commit is contained in:
Rory Powell 2021-09-14 11:17:54 +01:00 committed by GitHub
commit d2c5197fa0
4 changed files with 48 additions and 7 deletions

View File

@ -3,7 +3,27 @@ const { getTenantId, lookupTenantId, getGlobalDB } = require("../tenancy")
const EXPIRY_SECONDS = 3600 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) { if (!tenantId) {
try { try {
tenantId = getTenantId() tenantId = getTenantId()
@ -15,7 +35,7 @@ exports.getUser = async (userId, tenantId = null) => {
// try cache // try cache
let user = await client.get(userId) let user = await client.get(userId)
if (!user) { if (!user) {
user = await getGlobalDB(tenantId).get(userId) user = await populateUser(userId, tenantId)
client.store(userId, user, EXPIRY_SECONDS) client.store(userId, user, EXPIRY_SECONDS)
} }
if (user && !user.tenantId && tenantId) { if (user && !user.tenantId && tenantId) {

View File

@ -21,7 +21,10 @@ function finalise(
* The tenancy modules should not be used here and it should be assumed that the tenancy context * The tenancy modules should not be used here and it should be assumed that the tenancy context
* has not yet been populated. * has not yet been populated.
*/ */
module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => { module.exports = (
noAuthPatterns = [],
opts = { publicAllowed: false, populateUser: null }
) => {
const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : [] const noAuthOptions = noAuthPatterns ? buildMatcherRegex(noAuthPatterns) : []
return async (ctx, next) => { return async (ctx, next) => {
let publicEndpoint = false let publicEndpoint = false
@ -46,7 +49,15 @@ module.exports = (noAuthPatterns = [], opts = { publicAllowed: false }) => {
error = "No session found" error = "No session found"
} else { } else {
try { 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 delete user.password
authenticated = true authenticated = true
} catch (err) { } catch (err) {

View File

@ -8,6 +8,7 @@ const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD
exports.Databases = { exports.Databases = {
PW_RESETS: "pwReset", PW_RESETS: "pwReset",
VERIFICATIONS: "verification",
INVITATIONS: "invitation", INVITATIONS: "invitation",
DEV_LOCKS: "devLocks", DEV_LOCKS: "devLocks",
DEBOUNCE: "debounce", DEBOUNCE: "debounce",

View File

@ -33,7 +33,7 @@ async function allUsers() {
return response.rows.map(row => row.doc) return response.rows.map(row => row.doc)
} }
async function saveUser(user, tenantId) { async function saveUser(user, tenantId, hashPassword = true) {
if (!tenantId) { if (!tenantId) {
throw "No tenancy specified." throw "No tenancy specified."
} }
@ -56,7 +56,7 @@ async function saveUser(user, tenantId) {
// get the password, make sure one is defined // get the password, make sure one is defined
let hashedPassword let hashedPassword
if (password) { if (password) {
hashedPassword = await hash(password) hashedPassword = hashPassword ? await hash(password) : password
} else if (dbUser) { } else if (dbUser) {
hashedPassword = dbUser.password hashedPassword = dbUser.password
} else { } else {
@ -110,6 +110,15 @@ exports.save = async ctx => {
exports.adminUser = async ctx => { exports.adminUser = async ctx => {
const { email, password, tenantId } = ctx.request.body 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)) { if (await doesTenantExist(tenantId)) {
ctx.throw(403, "Organisation already exists.") ctx.throw(403, "Organisation already exists.")
} }
@ -141,7 +150,7 @@ exports.adminUser = async ctx => {
tenantId, tenantId,
} }
try { try {
ctx.body = await saveUser(user, tenantId) ctx.body = await saveUser(user, tenantId, hashPassword)
} catch (err) { } catch (err) {
ctx.throw(err.status || 400, err) ctx.throw(err.status || 400, err)
} }