diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index e2e42c20f9..c38f098db4 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -44,6 +44,7 @@ const { revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary") const { getTenantId, isMultiTenant } = require("@budibase/auth/tenancy") +const { syncGlobalUsers } = require("./user") const URL_REGEX_SLASH = /\/|\\/g @@ -328,6 +329,8 @@ exports.sync = async ctx => { if (!isDevAppID(appId)) { ctx.throw(400, "This action cannot be performed for production apps") } + + // replicate prod to dev const prodAppId = getDeployedAppID(appId) const replication = new Replication({ source: prodAppId, @@ -343,6 +346,10 @@ exports.sync = async ctx => { } catch (err) { error = err } + + // sync the users + await syncGlobalUsers(appId) + if (error) { ctx.throw(400, error) } else { diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 2bcdae4c05..53d8ecbacb 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -14,6 +14,8 @@ exports.fetchSelf = async ctx => { } const user = await getFullUser(ctx, userId) + // this shouldn't be returned by the app self + delete user.roles if (appId) { const db = new CouchDB(appId) @@ -36,6 +38,7 @@ exports.fetchSelf = async ctx => { // user has a role of some sort, return them else if (err.status === 404) { const metadata = { + ...user, _id: userId, } const dbResp = await db.put(metadata) diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 3ff67278b4..5ff1026a4d 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -6,25 +6,58 @@ const { const { InternalTables } = require("../../db/utils") const { getGlobalUsers } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") +const { isEqual } = require("lodash") +const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -function removeGlobalProps(user) { - // make sure to always remove some of the global user props - delete user.password - delete user.roles - delete user.builder - return user -} - -exports.fetchMetadata = async function (ctx) { - const database = new CouchDB(ctx.appId) - const global = await getGlobalUsers(ctx.appId) - const metadata = ( - await database.allDocs( +exports.rawMetadata = async db => { + return ( + await db.allDocs( getUserMetadataParams(null, { include_docs: true, }) ) ).rows.map(row => row.doc) +} + +exports.syncGlobalUsers = async appId => { + // sync user metadata + const db = new CouchDB(appId) + const [users, metadata] = await Promise.all([ + getGlobalUsers(appId), + exports.rawMetadata(db), + ]) + const toWrite = [] + for (let user of users) { + // skip users with no access + if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) { + continue + } + delete user._rev + const metadataId = generateUserMetadataID(user._id) + const newDoc = { + ...user, + _id: metadataId, + tableId: InternalTables.USER_METADATA, + } + const found = metadata.find(doc => doc._id === metadataId) + // copy rev over for the purposes of equality check + if (found) { + newDoc._rev = found._rev + } + if (found == null || !isEqual(newDoc, found)) { + toWrite.push({ + ...found, + ...newDoc, + }) + } + } + await db.bulkDocs(toWrite) +} + +exports.fetchMetadata = async function (ctx) { + const database = new CouchDB(ctx.appId) + const global = await getGlobalUsers(ctx.appId) + const metadata = await exports.rawMetadata(database) const users = [] for (let user of global) { // find the metadata that matches up to the global ID @@ -52,7 +85,9 @@ exports.updateSelfMetadata = async function (ctx) { exports.updateMetadata = async function (ctx) { const appId = ctx.appId const db = new CouchDB(appId) - const user = removeGlobalProps(ctx.request.body) + const user = ctx.request.body + // this isn't applicable to the user + delete user.roles const metadata = { tableId: InternalTables.USER_METADATA, ...user,