diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 61157689ac..b5e5242cb5 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -141,6 +141,18 @@ exports.getRoleParams = (roleId = null, otherProps = {}) => { return getDocParams(DocumentTypes.ROLE, roleId, otherProps) } +/** + * Convert a development app ID to a deployed app ID. + */ +exports.getDeployedAppID = appId => { + // if dev, convert it + if (appId.startsWith(exports.APP_DEV_PREFIX)) { + const id = appId.split(exports.APP_DEV_PREFIX)[1] + return `${exports.APP_PREFIX}${id}` + } + return appId +} + /** * Lots of different points in the system need to find the full list of apps, this will * enumerate the entire CouchDB cluster and get the list of databases (every app). diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js index aecc79eaa6..14296aa94b 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.js @@ -2,6 +2,7 @@ const fetch = require("node-fetch") const env = require("../environment") const { checkSlashesInUrl } = require("./index") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") +const { getDeployedAppID } = require("@budibase/auth/db") function getAppRole(appId, user) { if (!user.roles) { @@ -95,6 +96,8 @@ exports.deleteGlobalUser = async (ctx, globalId) => { } exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { + // always use the deployed app + appId = getDeployedAppID(appId) const endpoint = globalId ? `/api/admin/users/${globalId}` : `/api/admin/users` @@ -119,9 +122,14 @@ exports.saveGlobalUser = async (ctx, appId, body) => { const globalUser = body._id ? await exports.getGlobalUsers(ctx, appId, body._id) : {} - const roles = globalUser.roles || {} + const preRoles = globalUser.roles || {} if (body.roleId) { - roles[appId] = body.roleId + preRoles[appId] = body.roleId + } + // make sure no dev app IDs in roles + const roles = {} + for (let [appId, roleId] of Object.entries(preRoles)) { + roles[getDeployedAppID(appId)] = roleId } const endpoint = `/api/admin/users` const reqCfg = { diff --git a/packages/worker/src/api/controllers/admin/roles.js b/packages/worker/src/api/controllers/admin/roles.js index 3bb5647ca7..8ef45d3765 100644 --- a/packages/worker/src/api/controllers/admin/roles.js +++ b/packages/worker/src/api/controllers/admin/roles.js @@ -1,24 +1,37 @@ const { getAllRoles } = require("@budibase/auth/roles") -const { getAllApps } = require("@budibase/auth/db") +const { getAllApps, getDeployedAppID, DocumentTypes } = require("@budibase/auth/db") +const CouchDB = require("../../../db") exports.fetch = async ctx => { // always use the dev apps as they'll be most up to date (true) const apps = await getAllApps(true) const promises = [] for (let app of apps) { - promises.push(getAllRoles(app._id)) + // use dev app IDs + promises.push(getAllRoles(app.appId)) } const roles = await Promise.all(promises) const response = {} for (let app of apps) { - response[app._id] = roles.shift() + const deployedAppId = getDeployedAppID(app.appId) + response[deployedAppId] = { + roles: roles.shift(), + name: app.name, + version: app.version, + url: app.url, + } } ctx.body = response } exports.find = async ctx => { const appId = ctx.params.appId + const db = new CouchDB(appId) + const app = await db.get(DocumentTypes.APP_METADATA) ctx.body = { roles: await getAllRoles(appId), + name: app.name, + version: app.version, + url: app.url, } } diff --git a/packages/worker/src/constants/index.js b/packages/worker/src/constants/index.js index d133631c4f..b40446cc89 100644 --- a/packages/worker/src/constants/index.js +++ b/packages/worker/src/constants/index.js @@ -30,45 +30,65 @@ const EmailTemplatePurpose = { CUSTOM: "custom", } +const InternalTemplateBindings = { + PLATFORM_URL: "platformUrl", + COMPANY: "company", + LOGO_URL: "logoUrl", + EMAIL: "email", + USER: "user", + REQUEST: "request", + DOCS_URL: "docsUrl", + LOGIN_URL: "loginUrl", + CURRENT_YEAR: "currentYear", + CURRENT_DATE: "currentDate", + BODY: "body", + STYLES: "styles", + RESET_URL: "resetUrl", + RESET_CODE: "resetCode", + INVITE_URL: "inviteUrl", + INVITE_CODE: "inviteUrl", + CONTENTS: "contents", +} + const TemplateBindings = { PLATFORM_URL: { - name: "platformUrl", + name: InternalTemplateBindings.PLATFORM_URL, description: "The URL used to access the budibase platform", }, COMPANY: { - name: "company", + name: InternalTemplateBindings.COMPANY, description: "The name of your organization", }, LOGO_URL: { - name: "logoUrl", + name: InternalTemplateBindings.LOGO_URL, description: "The URL of your organizations logo.", }, EMAIL: { - name: "email", + name: InternalTemplateBindings.EMAIL, description: "The recipients email address.", }, USER: { - name: "user", + name: InternalTemplateBindings.USER, description: "The recipients user object.", }, REQUEST: { - name: "request", + name: InternalTemplateBindings.REQUEST, description: "Additional request metadata.", }, DOCS_URL: { - name: "docsUrl", + name: InternalTemplateBindings.DOCS_URL, description: "Organization documentation URL.", }, LOGIN_URL: { - name: "loginUrl", + name: InternalTemplateBindings.LOGIN_URL, description: "The URL used to log into the organization budibase instance.", }, CURRENT_YEAR: { - name: "currentYear", + name: InternalTemplateBindings.CURRENT_YEAR, description: "The current year.", }, CURRENT_DATE: { - name: "currentDate", + name: InternalTemplateBindings.CURRENT_DATE, description: "The current date.", }, } @@ -80,11 +100,11 @@ const TemplateMetadata = { purpose: EmailTemplatePurpose.BASE, bindings: [ { - name: "body", + name: InternalTemplateBindings.BODY, description: "The main body of another email template.", }, { - name: "styles", + name: InternalTemplateBindings.STYLES, description: "The contents of the Styling email template.", }, ], @@ -94,12 +114,12 @@ const TemplateMetadata = { purpose: EmailTemplatePurpose.PASSWORD_RECOVERY, bindings: [ { - name: "resetUrl", + name: InternalTemplateBindings.RESET_URL, description: "The URL the recipient must click to reset their password.", }, { - name: "resetCode", + name: InternalTemplateBindings.RESET_CODE, description: "The temporary password reset code used in the recipients password reset URL.", }, @@ -110,12 +130,12 @@ const TemplateMetadata = { purpose: EmailTemplatePurpose.INVITATION, bindings: [ { - name: "inviteUrl", + name: InternalTemplateBindings.INVITE_URL, description: "The URL the recipient must click to accept the invitation and activate their account.", }, { - name: "inviteCode", + name: InternalTemplateBindings.INVITE_CODE, description: "The temporary invite code used in the recipients invitation URL.", }, @@ -126,7 +146,7 @@ const TemplateMetadata = { purpose: EmailTemplatePurpose.CUSTOM, bindings: [ { - name: "contents", + name: InternalTemplateBindings.CONTENTS, description: "Custom content body.", }, ], @@ -142,4 +162,5 @@ exports.TemplateTypes = TemplateTypes exports.EmailTemplatePurpose = EmailTemplatePurpose exports.TemplateMetadata = TemplateMetadata exports.TemplateBindings = TemplateBindings +exports.InternalTemplateBindings = InternalTemplateBindings exports.GLOBAL_OWNER = "global" diff --git a/packages/worker/src/utilities/templates.js b/packages/worker/src/utilities/templates.js index 00ef9e6130..6b860e02bd 100644 --- a/packages/worker/src/utilities/templates.js +++ b/packages/worker/src/utilities/templates.js @@ -2,7 +2,7 @@ const CouchDB = require("../db") const { getScopedConfig, StaticDatabases } = require("@budibase/auth").db const { Configs, - TemplateBindings, + InternalTemplateBindings, LOGO_URL, EmailTemplatePurpose, } = require("../constants") @@ -21,27 +21,27 @@ exports.getSettingsTemplateContext = async (purpose, code = null) => { } const URL = settings.platformUrl const context = { - [TemplateBindings.LOGO_URL]: + [InternalTemplateBindings.LOGO_URL]: checkSlashesInUrl(`${URL}/${settings.logoUrl}`) || LOGO_URL, - [TemplateBindings.PLATFORM_URL]: URL, - [TemplateBindings.COMPANY]: settings.company || BASE_COMPANY, - [TemplateBindings.DOCS_URL]: + [InternalTemplateBindings.PLATFORM_URL]: URL, + [InternalTemplateBindings.COMPANY]: settings.company || BASE_COMPANY, + [InternalTemplateBindings.DOCS_URL]: settings.docsUrl || "https://docs.budibase.com/", - [TemplateBindings.LOGIN_URL]: checkSlashesInUrl(`${URL}/login`), - [TemplateBindings.CURRENT_DATE]: new Date().toISOString(), - [TemplateBindings.CURRENT_YEAR]: new Date().getFullYear(), + [InternalTemplateBindings.LOGIN_URL]: checkSlashesInUrl(`${URL}/login`), + [InternalTemplateBindings.CURRENT_DATE]: new Date().toISOString(), + [InternalTemplateBindings.CURRENT_YEAR]: new Date().getFullYear(), } // attach purpose specific context switch (purpose) { case EmailTemplatePurpose.PASSWORD_RECOVERY: - context[TemplateBindings.RESET_CODE] = code - context[TemplateBindings.RESET_URL] = checkSlashesInUrl( + context[InternalTemplateBindings.RESET_CODE] = code + context[InternalTemplateBindings.RESET_URL] = checkSlashesInUrl( `${URL}/reset?code=${code}` ) break case EmailTemplatePurpose.INVITATION: - context[TemplateBindings.INVITE_CODE] = code - context[TemplateBindings.INVITE_URL] = checkSlashesInUrl( + context[InternalTemplateBindings.INVITE_CODE] = code + context[InternalTemplateBindings.INVITE_URL] = checkSlashesInUrl( `${URL}/invite?code=${code}` ) break