2021-04-09 03:58:33 +12:00
|
|
|
const {
|
2021-04-10 02:11:49 +12:00
|
|
|
generateUserMetadataID,
|
|
|
|
getUserMetadataParams,
|
2021-12-08 07:24:10 +13:00
|
|
|
generateUserFlagID,
|
2021-04-10 02:11:49 +12:00
|
|
|
} = require("../../db/utils")
|
2021-04-09 03:58:33 +12:00
|
|
|
const { InternalTables } = require("../../db/utils")
|
2021-11-04 12:15:38 +13:00
|
|
|
const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
|
2021-04-13 02:54:14 +12:00
|
|
|
const { getFullUser } = require("../../utilities/users")
|
2021-11-04 11:23:00 +13:00
|
|
|
const { isEqual } = require("lodash")
|
2022-01-11 08:33:00 +13:00
|
|
|
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
|
|
|
const {
|
|
|
|
getDevelopmentAppID,
|
2022-02-01 06:42:51 +13:00
|
|
|
getProdAppIDs,
|
2022-01-28 13:05:39 +13:00
|
|
|
dbExists,
|
2022-01-11 08:33:00 +13:00
|
|
|
} = require("@budibase/backend-core/db")
|
|
|
|
const { UserStatus } = require("@budibase/backend-core/constants")
|
2022-02-05 06:37:13 +13:00
|
|
|
const { getAppDB, doInAppContext } = require("@budibase/backend-core/context")
|
2021-04-09 03:58:33 +12:00
|
|
|
|
2022-01-28 07:18:31 +13:00
|
|
|
async function rawMetadata() {
|
|
|
|
const db = getAppDB()
|
2021-11-04 11:23:00 +13:00
|
|
|
return (
|
|
|
|
await db.allDocs(
|
2021-04-10 02:11:49 +12:00
|
|
|
getUserMetadataParams(null, {
|
2020-12-09 06:33:08 +13:00
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2021-05-04 22:32:22 +12:00
|
|
|
).rows.map(row => row.doc)
|
2021-11-04 11:23:00 +13:00
|
|
|
}
|
|
|
|
|
2021-11-05 03:53:03 +13:00
|
|
|
function combineMetadataAndUser(user, metadata) {
|
2021-11-04 12:15:38 +13:00
|
|
|
// skip users with no access
|
|
|
|
if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
delete user._rev
|
|
|
|
const metadataId = generateUserMetadataID(user._id)
|
|
|
|
const newDoc = {
|
|
|
|
...user,
|
|
|
|
_id: metadataId,
|
|
|
|
tableId: InternalTables.USER_METADATA,
|
|
|
|
}
|
|
|
|
const found = Array.isArray(metadata)
|
|
|
|
? metadata.find(doc => doc._id === metadataId)
|
|
|
|
: metadata
|
|
|
|
// copy rev over for the purposes of equality check
|
|
|
|
if (found) {
|
|
|
|
newDoc._rev = found._rev
|
|
|
|
}
|
|
|
|
if (found == null || !isEqual(newDoc, found)) {
|
|
|
|
return {
|
|
|
|
...found,
|
|
|
|
...newDoc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2022-01-28 07:18:31 +13:00
|
|
|
exports.syncGlobalUsers = async () => {
|
2021-11-04 11:23:00 +13:00
|
|
|
// sync user metadata
|
2022-01-28 07:18:31 +13:00
|
|
|
const db = getAppDB()
|
|
|
|
const [users, metadata] = await Promise.all([getGlobalUsers(), rawMetadata()])
|
2021-11-04 11:23:00 +13:00
|
|
|
const toWrite = []
|
|
|
|
for (let user of users) {
|
2021-11-04 12:15:38 +13:00
|
|
|
const combined = await combineMetadataAndUser(user, metadata)
|
|
|
|
if (combined) {
|
|
|
|
toWrite.push(combined)
|
2021-11-04 11:23:00 +13:00
|
|
|
}
|
2021-11-04 12:15:38 +13:00
|
|
|
}
|
|
|
|
await db.bulkDocs(toWrite)
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.syncUser = async function (ctx) {
|
2021-11-05 03:53:03 +13:00
|
|
|
let deleting = false,
|
|
|
|
user
|
|
|
|
const userId = ctx.params.id
|
|
|
|
try {
|
|
|
|
user = await getRawGlobalUser(userId)
|
|
|
|
} catch (err) {
|
2021-11-11 01:00:29 +13:00
|
|
|
if (err && err.status === 404) {
|
|
|
|
user = {}
|
|
|
|
deleting = true
|
|
|
|
} else {
|
|
|
|
throw err
|
|
|
|
}
|
2021-11-05 03:53:03 +13:00
|
|
|
}
|
2021-12-01 22:54:23 +13:00
|
|
|
const roles = deleting ? {} : user.roles
|
2021-11-05 03:53:03 +13:00
|
|
|
// remove props which aren't useful to metadata
|
|
|
|
delete user.password
|
|
|
|
delete user.forceResetPassword
|
2021-11-04 12:15:38 +13:00
|
|
|
delete user.roles
|
2021-11-05 03:53:03 +13:00
|
|
|
// run through all production appIDs in the users roles
|
|
|
|
let prodAppIds
|
|
|
|
// if they are a builder then get all production app IDs
|
|
|
|
if ((user.builder && user.builder.global) || deleting) {
|
2022-02-01 06:42:51 +13:00
|
|
|
prodAppIds = await getProdAppIDs()
|
2021-11-05 03:53:03 +13:00
|
|
|
} else {
|
|
|
|
prodAppIds = Object.entries(roles)
|
|
|
|
.filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
|
|
|
|
.map(([appId]) => appId)
|
|
|
|
}
|
|
|
|
for (let prodAppId of prodAppIds) {
|
2021-11-16 05:46:56 +13:00
|
|
|
const roleId = roles[prodAppId]
|
2021-11-04 12:15:38 +13:00
|
|
|
const devAppId = getDevelopmentAppID(prodAppId)
|
|
|
|
for (let appId of [prodAppId, devAppId]) {
|
2022-01-28 13:05:39 +13:00
|
|
|
if (!(await dbExists(appId))) {
|
2021-11-04 12:15:38 +13:00
|
|
|
continue
|
|
|
|
}
|
2022-02-05 06:37:13 +13:00
|
|
|
await doInAppContext(appId, async () => {
|
|
|
|
const db = getAppDB()
|
|
|
|
const metadataId = generateUserMetadataID(userId)
|
|
|
|
let metadata
|
|
|
|
try {
|
|
|
|
metadata = await db.get(metadataId)
|
|
|
|
} catch (err) {
|
|
|
|
if (deleting) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
metadata = {
|
|
|
|
tableId: InternalTables.USER_METADATA,
|
|
|
|
}
|
2021-11-05 03:53:03 +13:00
|
|
|
}
|
2022-02-05 06:37:13 +13:00
|
|
|
// assign the roleId for the metadata doc
|
|
|
|
if (roleId) {
|
|
|
|
metadata.roleId = roleId
|
2021-11-05 03:53:03 +13:00
|
|
|
}
|
2022-02-05 06:37:13 +13:00
|
|
|
let combined = !deleting
|
|
|
|
? combineMetadataAndUser(user, metadata)
|
|
|
|
: {
|
|
|
|
...metadata,
|
|
|
|
status: UserStatus.INACTIVE,
|
|
|
|
metadata: BUILTIN_ROLE_IDS.PUBLIC,
|
|
|
|
}
|
|
|
|
// if its null then there was no updates required
|
|
|
|
if (combined) {
|
|
|
|
await db.put(combined)
|
|
|
|
}
|
|
|
|
})
|
2021-11-04 11:23:00 +13:00
|
|
|
}
|
|
|
|
}
|
2021-11-04 12:15:38 +13:00
|
|
|
ctx.body = {
|
|
|
|
message: "User synced.",
|
|
|
|
}
|
2021-11-04 11:23:00 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
exports.fetchMetadata = async function (ctx) {
|
2022-01-28 07:18:31 +13:00
|
|
|
const database = getAppDB()
|
|
|
|
const global = await getGlobalUsers()
|
2021-11-04 12:15:38 +13:00
|
|
|
const metadata = await rawMetadata(database)
|
2021-04-09 03:58:33 +12:00
|
|
|
const users = []
|
|
|
|
for (let user of global) {
|
2021-04-20 03:26:33 +12:00
|
|
|
// find the metadata that matches up to the global ID
|
2021-05-04 22:32:22 +12:00
|
|
|
const info = metadata.find(meta => meta._id.includes(user._id))
|
2021-04-14 04:11:55 +12:00
|
|
|
// remove these props, not for the correct DB
|
2021-04-09 03:58:33 +12:00
|
|
|
users.push({
|
|
|
|
...user,
|
|
|
|
...info,
|
2021-09-11 04:02:55 +12:00
|
|
|
tableId: InternalTables.USER_METADATA,
|
2021-04-10 03:55:56 +12:00
|
|
|
// make sure the ID is always a local ID, not a global one
|
2021-04-20 03:26:33 +12:00
|
|
|
_id: generateUserMetadataID(user._id),
|
2021-04-09 03:58:33 +12:00
|
|
|
})
|
2020-12-09 06:33:08 +13:00
|
|
|
}
|
|
|
|
ctx.body = users
|
2020-05-07 21:53:34 +12:00
|
|
|
}
|
2020-04-08 04:25:09 +12:00
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.updateSelfMetadata = async function (ctx) {
|
2021-04-21 04:17:44 +12:00
|
|
|
// overwrite the ID with current users
|
2021-04-22 22:45:22 +12:00
|
|
|
ctx.request.body._id = ctx.user._id
|
2021-04-21 04:17:44 +12:00
|
|
|
// make sure no stale rev
|
|
|
|
delete ctx.request.body._rev
|
2022-01-26 11:54:50 +13:00
|
|
|
// make sure no csrf token
|
|
|
|
delete ctx.request.body.csrfToken
|
2021-04-21 04:17:44 +12:00
|
|
|
await exports.updateMetadata(ctx)
|
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.updateMetadata = async function (ctx) {
|
2022-01-28 07:18:31 +13:00
|
|
|
const db = getAppDB()
|
2021-11-04 11:23:00 +13:00
|
|
|
const user = ctx.request.body
|
|
|
|
// this isn't applicable to the user
|
|
|
|
delete user.roles
|
2021-04-20 03:26:33 +12:00
|
|
|
const metadata = {
|
2021-04-30 06:06:58 +12:00
|
|
|
tableId: InternalTables.USER_METADATA,
|
2021-05-20 02:55:00 +12:00
|
|
|
...user,
|
2021-04-09 03:58:33 +12:00
|
|
|
}
|
2021-04-20 03:26:33 +12:00
|
|
|
ctx.body = await db.put(metadata)
|
2020-06-26 21:05:09 +12:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.destroyMetadata = async function (ctx) {
|
2022-01-28 07:18:31 +13:00
|
|
|
const db = getAppDB()
|
2021-04-10 03:55:56 +12:00
|
|
|
try {
|
2021-04-20 03:26:33 +12:00
|
|
|
const dbUser = await db.get(ctx.params.id)
|
2021-04-10 03:55:56 +12:00
|
|
|
await db.remove(dbUser._id, dbUser._rev)
|
|
|
|
} catch (err) {
|
|
|
|
// error just means the global user has no config in this app
|
|
|
|
}
|
2021-03-10 06:09:18 +13:00
|
|
|
ctx.body = {
|
2021-05-20 00:17:50 +12:00
|
|
|
message: `User metadata ${ctx.params.id} deleted.`,
|
2021-03-10 06:09:18 +13:00
|
|
|
}
|
2020-05-15 02:12:30 +12:00
|
|
|
}
|
|
|
|
|
2021-05-03 19:31:09 +12:00
|
|
|
exports.findMetadata = async function (ctx) {
|
2021-04-20 03:26:33 +12:00
|
|
|
ctx.body = await getFullUser(ctx, ctx.params.id)
|
2020-05-07 21:53:34 +12:00
|
|
|
}
|
2021-12-08 07:24:10 +13:00
|
|
|
|
|
|
|
exports.setFlag = async function (ctx) {
|
|
|
|
const userId = ctx.user._id
|
|
|
|
const { flag, value } = ctx.request.body
|
|
|
|
if (!flag) {
|
|
|
|
ctx.throw(400, "Must supply a 'flag' field in request body.")
|
|
|
|
}
|
|
|
|
const flagDocId = generateUserFlagID(userId)
|
2022-01-28 07:18:31 +13:00
|
|
|
const db = getAppDB()
|
2021-12-08 07:24:10 +13:00
|
|
|
let doc
|
|
|
|
try {
|
|
|
|
doc = await db.get(flagDocId)
|
|
|
|
} catch (err) {
|
|
|
|
doc = { _id: flagDocId }
|
|
|
|
}
|
|
|
|
doc[flag] = value || true
|
|
|
|
await db.put(doc)
|
|
|
|
ctx.body = { message: "Flag set successfully" }
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.getFlags = async function (ctx) {
|
|
|
|
const userId = ctx.user._id
|
|
|
|
const docId = generateUserFlagID(userId)
|
2022-01-28 07:18:31 +13:00
|
|
|
const db = getAppDB()
|
2021-12-08 07:24:10 +13:00
|
|
|
let doc
|
|
|
|
try {
|
|
|
|
doc = await db.get(docId)
|
|
|
|
} catch (err) {
|
|
|
|
doc = { _id: docId }
|
|
|
|
}
|
|
|
|
ctx.body = doc
|
|
|
|
}
|