UNPKG

@budibase/server

Version:
162 lines (153 loc) 4.25 kB
import { context, roles as rolesCore } from "@budibase/backend-core" import { ContextUser, ContextUserMetadata, Database, isSSOUser, UserBindings, UserMetadata, } from "@budibase/types" import isEqual from "lodash/isEqual" import { generateUserMetadataID, getGlobalIDFromUserMetadataID, getUserMetadataParams, InternalTables, } from "../../db/utils" import { getGlobalUsers } from "../../utilities/global" export function combineMetadataAndUser( user: ContextUser, metadata: UserMetadata | UserMetadata[] ): ContextUserMetadata | null { const metadataId = generateUserMetadataID(user._id!) const found = Array.isArray(metadata) ? metadata.find(doc => doc._id === metadataId) : metadata // skip users with no access if ( user.roleId == null || user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC ) { // If it exists and it should not, we must remove it if (found?._id) { return { ...found, _deleted: true } } return null } delete user._rev const newDoc = { ...user, _id: metadataId, tableId: InternalTables.USER_METADATA, } // copy rev over for the purposes of equality check if (found) { newDoc._rev = found._rev newDoc.createdAt = found.createdAt newDoc.updatedAt = found.updatedAt } // clear fields that shouldn't be in metadata delete newDoc.password delete newDoc.forceResetPassword delete newDoc.roles if (found == null || !isEqual(newDoc, found)) { return { ...found, ...newDoc, createdAt: found?.createdAt ?? (new Date().toISOString() as any), updatedAt: new Date().toISOString(), } } return null } export async function rawUserMetadata(db?: Database): Promise<UserMetadata[]> { if (!db) { db = context.getWorkspaceDB() } return ( await db.allDocs<UserMetadata>( getUserMetadataParams(null, { include_docs: true, }) ) ).rows.map(row => row.doc!) } export async function fetchMetadata(): Promise<ContextUserMetadata[]> { const global = await getGlobalUsers() const metadata = await rawUserMetadata() const users: ContextUserMetadata[] = [] for (let user of global) { // find the metadata that matches up to the global ID const info = metadata.find(meta => meta._id!.includes(user._id!)) // remove these props, not for the correct DB users.push({ ...user, ...info, tableId: InternalTables.USER_METADATA, // make sure the ID is always a local ID, not a global one _id: generateUserMetadataID(user._id!), }) } return users } export async function syncGlobalUsers() { // sync user metadata const dbs = [context.getDevWorkspaceDB(), context.getProdWorkspaceDB()] for (let db of dbs) { if (!(await db.exists())) { continue } const [users, metadata] = await Promise.all([ getGlobalUsers(), rawUserMetadata(db), ]) const toWrite = [] for (let user of users) { const combined = combineMetadataAndUser(user, metadata) if (combined) { toWrite.push(combined) } } let foundEmails: string[] = [] for (let data of metadata) { if (!data._id) { continue } const alreadyExisting = data.email && foundEmails.indexOf(data.email) !== -1 const globalId = getGlobalIDFromUserMetadataID(data._id) if (!users.find(user => user._id === globalId) || alreadyExisting) { toWrite.push({ ...data, _deleted: true }) } if (data.email) { foundEmails.push(data.email) } } await db.bulkDocs(toWrite) } } export function getUserContextBindings(user: ContextUser): UserBindings { if (!user) { return {} } const bindings: UserBindings = { _id: user._id, _rev: user._rev, firstName: user.firstName, lastName: user.lastName, email: user.email, status: user.status, roleId: user.roleId, globalId: user.globalId, userId: user.userId, } if (isSSOUser(user) && user.oauth2) { bindings.oauth2 = { accessToken: user.oauth2.accessToken, refreshToken: user.oauth2.refreshToken, } bindings.provider = user.provider bindings.providerType = user.providerType } return bindings }