From 311fe5f2eb939de78cecba338f7c0293a972898a Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Wed, 3 Nov 2021 22:23:00 +0000 Subject: [PATCH 1/6] Syncing user metadata when builder opened as well as including all global user props for filtering/searching. --- .../server/src/api/controllers/application.js | 7 +++ packages/server/src/api/controllers/auth.js | 3 + packages/server/src/api/controllers/user.js | 63 ++++++++++++++----- 3 files changed, 59 insertions(+), 14 deletions(-) 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, From 3a5e004f36167e5970ff1e4bfe6fe92543673fa6 Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Wed, 3 Nov 2021 23:15:38 +0000 Subject: [PATCH 2/6] Adding sync user endpoint to server which can be used by the worker. --- packages/auth/src/db/utils.js | 11 +++ packages/server/src/api/controllers/user.js | 87 +++++++++++++++------ packages/server/src/api/routes/user.js | 5 ++ packages/server/src/utilities/global.js | 8 +- packages/server/src/utilities/index.js | 6 ++ 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index fa162603e6..03bd773922 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -152,6 +152,17 @@ exports.getDeployedAppID = appId => { return appId } +/** + * Convert a deployed app ID to a development app ID. + */ +exports.getDevelopmentAppID = appId => { + if (!appId.startsWith(exports.APP_DEV_PREFIX)) { + const id = appId.split(exports.APP_PREFIX)[1] + return `${exports.APP_DEV_PREFIX}${id}` + } + return appId +} + exports.getCouchUrl = () => { if (!env.COUCH_DB_URL) return diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 5ff1026a4d..0362d1298b 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -4,12 +4,14 @@ const { getUserMetadataParams, } = require("../../db/utils") const { InternalTables } = require("../../db/utils") -const { getGlobalUsers } = require("../../utilities/global") +const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") const { isEqual } = require("lodash") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") +const { getDevelopmentAppID } = require("@budibase/auth/db") +const { doesDatabaseExist } = require("../../utilities") -exports.rawMetadata = async db => { +async function rawMetadata(db) { return ( await db.allDocs( getUserMetadataParams(null, { @@ -19,45 +21,80 @@ exports.rawMetadata = async db => { ).rows.map(row => row.doc) } +async function combineMetadataAndUser(user, metadata) { + // 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 +} + exports.syncGlobalUsers = async appId => { // sync user metadata const db = new CouchDB(appId) const [users, metadata] = await Promise.all([ getGlobalUsers(appId), - exports.rawMetadata(db), + 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, - }) + const combined = await combineMetadataAndUser(user, metadata) + if (combined) { + toWrite.push(combined) } } await db.bulkDocs(toWrite) } +exports.syncUser = async function (ctx) { + const user = await getRawGlobalUser(ctx.params.id) + const roles = user.roles + delete user.roles + for (let [prodAppId, roleId] of Object.entries(roles)) { + if (roleId === BUILTIN_ROLE_IDS.PUBLIC) { + continue + } + const devAppId = getDevelopmentAppID(prodAppId) + for (let appId of [prodAppId, devAppId]) { + if (!(await doesDatabaseExist(appId))) { + continue + } + const db = new CouchDB(appId) + const userId = generateUserMetadataID(user._id) + const metadata = await db.get(userId) + const combined = combineMetadataAndUser(user, metadata) + await db.put(combined) + } + } + ctx.body = { + message: "User synced.", + } +} + exports.fetchMetadata = async function (ctx) { const database = new CouchDB(ctx.appId) const global = await getGlobalUsers(ctx.appId) - const metadata = await exports.rawMetadata(database) + const metadata = await rawMetadata(database) const users = [] for (let user of global) { // find the metadata that matches up to the global ID diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js index d171870215..43c08a7f33 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.js @@ -34,5 +34,10 @@ router authorized(PermissionTypes.USER, PermissionLevels.WRITE), controller.destroyMetadata ) + .post( + "/api/users/sync/:id", + authorized(PermissionTypes.USER, PermissionLevels.WRITE), + controller.syncUser + ) module.exports = router diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index 4b9bbcba8c..eb64aa7f77 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -46,9 +46,13 @@ exports.getCachedSelf = async (ctx, appId) => { return processUser(appId, user) } -exports.getGlobalUser = async (appId, userId) => { +exports.getRawGlobalUser = async userId => { const db = getGlobalDB() - let user = await db.get(getGlobalIDFromUserMetadataID(userId)) + return db.get(getGlobalIDFromUserMetadataID(userId)) +} + +exports.getGlobalUser = async (appId, userId) => { + let user = await exports.getRawGlobalUser(userId) return processUser(appId, user) } diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index e568ba063c..d67c8f1da0 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -134,3 +134,9 @@ exports.stringToReadStream = string => { }, }) } + +exports.doesDatabaseExist = async dbName => { + const db = new CouchDB(dbName, { skip_setup: true }) + const info = await db.info() + return info && !info.error +} From 94ee13ffc4da6bffb7c919f9db23d50ffecb689a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Nov 2021 14:53:03 +0000 Subject: [PATCH 3/6] Adding the sync call from the worker for creation, updating and deletion of users. Making sure that production and development apps are always up to date with user metadata. --- hosting/docker-compose.yaml | 1 + .../templates/worker-service-deployment.yaml | 2 + packages/server/src/api/controllers/user.js | 64 ++++++++++++++++--- packages/server/src/middleware/authorized.js | 3 +- packages/server/src/utilities/global.js | 1 + packages/server/src/utilities/index.js | 10 ++- packages/worker/scripts/dev/manage.js | 1 + .../src/api/controllers/global/users.js | 8 ++- packages/worker/src/environment.js | 8 +++ packages/worker/src/utilities/appService.js | 23 +++++++ 10 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 packages/worker/src/utilities/appService.js diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 66b24f4e49..c94d1520a1 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -45,6 +45,7 @@ services: MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} MINIO_URL: http://minio-service:9000 + APPS_URL: http://app-service:4002 COUCH_DB_USERNAME: ${COUCH_DB_USER} COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD} COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984 diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index d7d8702567..6cded8545f 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -113,6 +113,8 @@ spec: value: {{ .Values.globals.smtp.port | quote }} - name: SMTP_FROM_ADDRESS value: {{ .Values.globals.smtp.from | quote }} + - name: APPS_URL + value: http://app-service:{{ .Values.services.apps.port }} image: budibase/worker imagePullPolicy: Always name: bbworker diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 0362d1298b..5faf821349 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -8,8 +8,13 @@ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") const { isEqual } = require("lodash") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { getDevelopmentAppID } = require("@budibase/auth/db") +const { + getDevelopmentAppID, + getAllApps, + isDevAppID, +} = require("@budibase/auth/db") const { doesDatabaseExist } = require("../../utilities") +const { UserStatus } = require("@budibase/auth/constants") async function rawMetadata(db) { return ( @@ -21,7 +26,7 @@ async function rawMetadata(db) { ).rows.map(row => row.doc) } -async function combineMetadataAndUser(user, metadata) { +function combineMetadataAndUser(user, metadata) { // skip users with no access if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) { return null @@ -67,22 +72,61 @@ exports.syncGlobalUsers = async appId => { } exports.syncUser = async function (ctx) { - const user = await getRawGlobalUser(ctx.params.id) + let deleting = false, + user + const userId = ctx.params.id + try { + user = await getRawGlobalUser(userId) + } catch (err) { + user = {} + deleting = true + } const roles = user.roles + // remove props which aren't useful to metadata + delete user.password + delete user.forceResetPassword delete user.roles - for (let [prodAppId, roleId] of Object.entries(roles)) { - if (roleId === BUILTIN_ROLE_IDS.PUBLIC) { - continue - } + // 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) { + prodAppIds = (await getAllApps(CouchDB, { idsOnly: true })).filter( + id => !isDevAppID(id) + ) + } else { + prodAppIds = Object.entries(roles) + .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC) + .map(([appId]) => appId) + } + for (let prodAppId of prodAppIds) { const devAppId = getDevelopmentAppID(prodAppId) for (let appId of [prodAppId, devAppId]) { if (!(await doesDatabaseExist(appId))) { continue } const db = new CouchDB(appId) - const userId = generateUserMetadataID(user._id) - const metadata = await db.get(userId) - const combined = combineMetadataAndUser(user, metadata) + const metadataId = generateUserMetadataID(userId) + let metadata + try { + metadata = await db.get(metadataId) + } catch (err) { + if (deleting) { + continue + } + metadata = { + tableId: InternalTables.USER_METADATA, + } + } + let combined + if (deleting) { + combined = { + ...metadata, + status: UserStatus.INACTIVE, + metadata: BUILTIN_ROLE_IDS.PUBLIC, + } + } else { + combined = combineMetadataAndUser(user, metadata) + } await db.put(combined) } } diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js index bd064f7e66..a30e034be2 100644 --- a/packages/server/src/middleware/authorized.js +++ b/packages/server/src/middleware/authorized.js @@ -18,7 +18,8 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => { // webhooks don't need authentication, each webhook unique - if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) { + // also internal requests (between services) don't need authorized + if (WEBHOOK_ENDPOINTS.test(ctx.request.url) || ctx.internal) { return next() } diff --git a/packages/server/src/utilities/global.js b/packages/server/src/utilities/global.js index eb64aa7f77..6527aa0601 100644 --- a/packages/server/src/utilities/global.js +++ b/packages/server/src/utilities/global.js @@ -77,6 +77,7 @@ exports.getGlobalUsers = async (appId = null, users = null) => { .filter(user => user != null) .map(user => { delete user.password + delete user.forceResetPassword return user }) if (!appId) { diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index d67c8f1da0..266ee09f9f 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -136,7 +136,11 @@ exports.stringToReadStream = string => { } exports.doesDatabaseExist = async dbName => { - const db = new CouchDB(dbName, { skip_setup: true }) - const info = await db.info() - return info && !info.error + try { + const db = new CouchDB(dbName, { skip_setup: true }) + const info = await db.info() + return info && !info.error + } catch (err) { + return false + } } diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index e0b8c3586a..179167883f 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -25,6 +25,7 @@ async function init() { ACCOUNT_PORTAL_URL: "http://localhost:10001", ACCOUNT_PORTAL_API_KEY: "budibase", PLATFORM_URL: "http://localhost:10000", + APPS_URL: "http://localhost:4001", } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/worker/src/api/controllers/global/users.js b/packages/worker/src/api/controllers/global/users.js index ed70d6122e..42166faad7 100644 --- a/packages/worker/src/api/controllers/global/users.js +++ b/packages/worker/src/api/controllers/global/users.js @@ -19,6 +19,7 @@ const { } = require("@budibase/auth/tenancy") const { removeUserFromInfoDB } = require("@budibase/auth/deprovision") const env = require("../../../environment") +const { syncUserInApps } = require("../../../utilities/appService") async function allUsers() { const db = getGlobalDB() @@ -32,7 +33,10 @@ async function allUsers() { exports.save = async ctx => { try { - ctx.body = await saveUser(ctx.request.body, getTenantId()) + const user = await saveUser(ctx.request.body, getTenantId()) + // let server know to sync user + await syncUserInApps(user._id) + ctx.body = user } catch (err) { ctx.throw(err.status || 400, err) } @@ -129,6 +133,8 @@ exports.destroy = async ctx => { await db.remove(dbUser._id, dbUser._rev) await userCache.invalidateUser(dbUser._id) await invalidateSessions(dbUser._id) + // let server know to sync user + await syncUserInApps(dbUser._id) ctx.body = { message: `User ${ctx.params.id} deleted.`, } diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index a1fab84112..91f06ea46d 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -42,6 +42,7 @@ module.exports = { SMTP_FROM_ADDRESS: process.env.SMTP_FROM_ADDRESS, PLATFORM_URL: process.env.PLATFORM_URL, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, + APPS_URL: process.env.APPS_URL, _set(key, value) { process.env[key] = value module.exports[key] = value @@ -53,6 +54,13 @@ module.exports = { }, } +// if some var haven't been set, define them +if (!module.exports.APPS_URL) { + module.exports.APPS_URL = isDev() + ? "http://localhost:4001" + : "http://app-service:4002" +} + // clean up any environment variable edge cases for (let [key, value] of Object.entries(module.exports)) { // handle the edge case of "0" to disable an environment variable diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js new file mode 100644 index 0000000000..a7be05b227 --- /dev/null +++ b/packages/worker/src/utilities/appService.js @@ -0,0 +1,23 @@ +const fetch = require("node-fetch") +const { Headers } = require("@budibase/auth/constants") +const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") +const { checkSlashesInUrl } = require("../utilities") +const env = require("../environment") + +exports.syncUserInApps = async userId => { + const request = { headers: {} } + request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY + if (isTenantIdSet()) { + request.headers[Headers.TENANT_ID] = getTenantId() + } + request.headers["Content-Type"] = "application/json" + request.body = JSON.stringify({}) + request.method = "POST" + const response = await fetch( + checkSlashesInUrl(env.APPS_URL + `/api/users/sync/${userId}`), + request + ) + if (response.status !== 200) { + throw "Unable to sync user." + } +} From 6e76a7dd1af0056c2a885bc43be94e12c8e57106 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 4 Nov 2021 20:28:26 +0000 Subject: [PATCH 4/6] Adding a check to disable user sync in test when server not available. --- packages/worker/src/utilities/appService.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index a7be05b227..166a82f78b 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -5,6 +5,9 @@ const { checkSlashesInUrl } = require("../utilities") const env = require("../environment") exports.syncUserInApps = async userId => { + if (env.isTest()) { + return + } const request = { headers: {} } request.headers[Headers.API_KEY] = env.INTERNAL_API_KEY if (isTenantIdSet()) { From 47122f76fd1fb961cd929ed963acf51cbe7f9512 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Nov 2021 12:00:29 +0000 Subject: [PATCH 5/6] Updating per review comments. --- packages/auth/src/db/utils.js | 18 ++++++++++++++++++ packages/server/src/api/controllers/user.js | 18 ++++++++---------- packages/server/src/api/routes/user.js | 2 +- packages/worker/src/utilities/appService.js | 21 ++++++++++++++------- 4 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 03bd773922..b956089660 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -259,6 +259,24 @@ exports.getAllApps = async (CouchDB, { dev, all, idsOnly } = {}) => { } } +/** + * Utility function for getAllApps but filters to production apps only. + */ +exports.getDeployedAppIDs = async CouchDB => { + return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter( + id => !exports.isDevAppID(id) + ) +} + +/** + * Utility function for the inverse of above. + */ +exports.getDevAppIDs = async CouchDB => { + return (await exports.getAllApps(CouchDB, { idsOnly: true })).filter(id => + exports.isDevAppID(id) + ) +} + exports.dbExists = async (CouchDB, dbName) => { let exists = false try { diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index 5faf821349..908018fe51 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -8,11 +8,7 @@ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global") const { getFullUser } = require("../../utilities/users") const { isEqual } = require("lodash") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { - getDevelopmentAppID, - getAllApps, - isDevAppID, -} = require("@budibase/auth/db") +const { getDevelopmentAppID, getDeployedAppIDs } = require("@budibase/auth/db") const { doesDatabaseExist } = require("../../utilities") const { UserStatus } = require("@budibase/auth/constants") @@ -78,8 +74,12 @@ exports.syncUser = async function (ctx) { try { user = await getRawGlobalUser(userId) } catch (err) { - user = {} - deleting = true + if (err && err.status === 404) { + user = {} + deleting = true + } else { + throw err + } } const roles = user.roles // remove props which aren't useful to metadata @@ -90,9 +90,7 @@ exports.syncUser = async function (ctx) { let prodAppIds // if they are a builder then get all production app IDs if ((user.builder && user.builder.global) || deleting) { - prodAppIds = (await getAllApps(CouchDB, { idsOnly: true })).filter( - id => !isDevAppID(id) - ) + prodAppIds = await getDeployedAppIDs(CouchDB) } else { prodAppIds = Object.entries(roles) .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC) diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js index 43c08a7f33..a3043b5af1 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.js @@ -35,7 +35,7 @@ router controller.destroyMetadata ) .post( - "/api/users/sync/:id", + "/api/users/metadata/sync/:id", authorized(PermissionTypes.USER, PermissionLevels.WRITE), controller.syncUser ) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index 166a82f78b..92809c9046 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -4,7 +4,7 @@ const { getTenantId, isTenantIdSet } = require("@budibase/auth/tenancy") const { checkSlashesInUrl } = require("../utilities") const env = require("../environment") -exports.syncUserInApps = async userId => { +async function makeAppRequest(url, method, body) { if (env.isTest()) { return } @@ -13,12 +13,19 @@ exports.syncUserInApps = async userId => { if (isTenantIdSet()) { request.headers[Headers.TENANT_ID] = getTenantId() } - request.headers["Content-Type"] = "application/json" - request.body = JSON.stringify({}) - request.method = "POST" - const response = await fetch( - checkSlashesInUrl(env.APPS_URL + `/api/users/sync/${userId}`), - request + if (body) { + request.headers["Content-Type"] = "application/json" + request.body = JSON.stringify(body) + } + request.method = method + return fetch(checkSlashesInUrl(env.APPS_URL + url), request) +} + +exports.syncUserInApps = async userId => { + const response = await makeAppRequest( + `/api/users/metadata/sync/${userId}`, + "POST", + {} ) if (response.status !== 200) { throw "Unable to sync user." From 18b98a158a034f059b01009bc47720865a687ab4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Nov 2021 12:52:23 +0000 Subject: [PATCH 6/6] Fixing issue found by test case. --- packages/worker/src/utilities/appService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker/src/utilities/appService.js b/packages/worker/src/utilities/appService.js index 92809c9046..23c5581510 100644 --- a/packages/worker/src/utilities/appService.js +++ b/packages/worker/src/utilities/appService.js @@ -27,7 +27,7 @@ exports.syncUserInApps = async userId => { "POST", {} ) - if (response.status !== 200) { + if (response && response.status !== 200) { throw "Unable to sync user." } }