From 0bbd45b413d9e74a042b24e202ef27a9d66eb28f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 19 May 2021 15:09:57 +0100 Subject: [PATCH] Adding the ability to get all apps, with the status attached. --- packages/auth/src/db/utils.js | 25 +++++--- packages/bbui/src/Typography/Heading.svelte | 2 +- .../src/components/deploy/DeployModal.svelte | 2 +- .../server/src/api/controllers/application.js | 10 +++- packages/server/src/api/controllers/user.js | 19 ++++--- .../server/src/api/routes/tests/user.spec.js | 6 -- packages/server/src/db/utils.js | 1 + .../server/src/utilities/workerRequests.js | 57 +++++++------------ .../worker/src/api/controllers/admin/roles.js | 2 +- .../worker/src/api/controllers/admin/users.js | 14 +++-- .../worker/src/api/routes/admin/configs.js | 10 +++- .../worker/src/api/routes/admin/groups.js | 10 +++- packages/worker/src/api/routes/admin/roles.js | 5 +- .../worker/src/api/routes/admin/templates.js | 10 +++- packages/worker/src/api/routes/admin/users.js | 44 +++++++------- packages/worker/src/middleware/adminOnly.js | 2 +- 16 files changed, 123 insertions(+), 96 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 91a682d859..e33b2b719f 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -34,6 +34,10 @@ exports.APP_PREFIX = DocumentTypes.APP + SEPARATOR exports.APP_DEV_PREFIX = DocumentTypes.APP_DEV + SEPARATOR exports.SEPARATOR = SEPARATOR +function isDevApp(app) { + return app.appId.startsWith(exports.APP_DEV_PREFIX) +} + /** * If creating DB allDocs/query params with only a single top level ID this can be used, this * is usually the case as most of our docs are top level e.g. tables, automations, users and so on. @@ -160,7 +164,7 @@ exports.getDeployedAppID = appId => { * different users/companies apps as there is no security around it - all apps are returned. * @return {Promise} returns the app information document stored in each app database. */ -exports.getAllApps = async (devApps = false) => { +exports.getAllApps = async ({ dev, all } = {}) => { const CouchDB = getCouch() let allDbs = await CouchDB.allDbs() const appDbNames = allDbs.filter(dbName => @@ -176,12 +180,19 @@ exports.getAllApps = async (devApps = false) => { const apps = response .filter(result => result.status === "fulfilled") .map(({ value }) => value) - return apps.filter(app => { - if (devApps) { - return app.appId.startsWith(exports.APP_DEV_PREFIX) - } - return !app.appId.startsWith(exports.APP_DEV_PREFIX) - }) + if (!all) { + return apps.filter(app => { + if (dev) { + return isDevApp(app) + } + return !isDevApp(app) + }) + } else { + return apps.map(app => ({ + ...app, + status: isDevApp(app) ? "development" : "published" + })) + } } } diff --git a/packages/bbui/src/Typography/Heading.svelte b/packages/bbui/src/Typography/Heading.svelte index 72f3ba2a8f..ff659a8861 100644 --- a/packages/bbui/src/Typography/Heading.svelte +++ b/packages/bbui/src/Typography/Heading.svelte @@ -8,7 +8,7 @@

diff --git a/packages/builder/src/components/deploy/DeployModal.svelte b/packages/builder/src/components/deploy/DeployModal.svelte index 869f4d02ce..850d72c5be 100644 --- a/packages/builder/src/components/deploy/DeployModal.svelte +++ b/packages/builder/src/components/deploy/DeployModal.svelte @@ -12,7 +12,7 @@ FAILURE: "FAILURE", } - const POLL_INTERVAL = 1000 + const POLL_INTERVAL = 10000 let loading = false let feedbackModal diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index b9f9dd8c5a..51670fb58f 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -117,13 +117,17 @@ async function createInstance(template) { } exports.fetch = async function (ctx) { - const isDev = ctx.query && ctx.query.status === AppStatus.DEV - const apps = await getAllApps(isDev) + const dev = ctx.query && ctx.query.status === AppStatus.DEV + const all = ctx.query && ctx.query.status === AppStatus.ALL + const apps = await getAllApps({ dev, all }) // get the locks for all the dev apps - if (isDev) { + if (dev || all) { const locks = await getAllLocks() for (let app of apps) { + if (app.status !== "development") { + continue + } const lock = locks.find(lock => lock.appId === app.appId) if (lock) { app.lockedBy = lock.user diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index e959ae6cae..6f346a3a11 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -5,9 +5,17 @@ const { } = require("../../db/utils") const { InternalTables } = require("../../db/utils") const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles") -const { getGlobalUsers } = require("../../utilities/workerRequests") +const { getGlobalUsers, addAppRoleToSelf } = require("../../utilities/workerRequests") const { getFullUser } = require("../../utilities/users") +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, ctx.appId) @@ -37,7 +45,8 @@ exports.updateSelfMetadata = async function (ctx) { // overwrite the ID with current users ctx.request.body._id = ctx.user._id if (ctx.user.builder && ctx.user.builder.global) { - ctx.request.body.roleId = BUILTIN_ROLE_IDS.ADMIN + // specific case, update self role in global user + await addAppRoleToSelf(ctx, ctx.appId, BUILTIN_ROLE_IDS.ADMIN) } // make sure no stale rev delete ctx.request.body._rev @@ -47,11 +56,7 @@ exports.updateSelfMetadata = async function (ctx) { exports.updateMetadata = async function (ctx) { const appId = ctx.appId const db = new CouchDB(appId) - const user = ctx.request.body - // make sure to always remove some of the global user props - delete user.password - delete user.roles - delete user.builder + const user = removeGlobalProps(ctx.request.body) const metadata = { tableId: InternalTables.USER_METADATA, _id: user._id, diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index 4581cdf798..0259b2bc87 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -117,9 +117,6 @@ describe("/users", () => { describe("update", () => { beforeEach(() => { - workerRequests.saveGlobalUser.mockImplementationOnce(() => ({ - _id: "us_test@test.com" - })) }) it("should be able to update the user", async () => { @@ -151,9 +148,6 @@ describe("/users", () => { describe("find", () => { beforeEach(() => { jest.resetAllMocks() - workerRequests.saveGlobalUser.mockImplementationOnce(() => ({ - _id: "us_uuid1", - })) workerRequests.getGlobalUsers.mockImplementationOnce(() => ({ _id: "us_uuid1", roleId: BUILTIN_ROLE_IDS.POWER, diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index ed63dff2a4..eacf0cbc6f 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -19,6 +19,7 @@ const StaticDatabases = { const AppStatus = { DEV: "dev", + ALL: "all", DEPLOYED: "PUBLISHED", } diff --git a/packages/server/src/utilities/workerRequests.js b/packages/server/src/utilities/workerRequests.js index 14296aa94b..e53838ed39 100644 --- a/packages/server/src/utilities/workerRequests.js +++ b/packages/server/src/utilities/workerRequests.js @@ -118,53 +118,38 @@ exports.getGlobalUsers = async (ctx, appId = null, globalId = null) => { return users } -exports.saveGlobalUser = async (ctx, appId, body) => { - const globalUser = body._id - ? await exports.getGlobalUsers(ctx, appId, body._id) - : {} - const preRoles = globalUser.roles || {} - if (body.roleId) { - preRoles[appId] = body.roleId +exports.getGlobalSelf = async ctx => { + const endpoint = `/api/admin/users/self` + const response = await fetch( + checkSlashesInUrl(env.WORKER_URL + endpoint), + request(ctx, { method: "GET" }) + ) + const json = await response.json() + if (json.status !== 200 && response.status !== 200) { + ctx.throw(400, "Unable to get self globally.") } - // 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` + return json +} + +exports.addAppRoleToSelf = async (ctx, appId, roleId) => { + const self = await exports.getGlobalSelf(ctx) + const endpoint = `/api/admin/users/self` const reqCfg = { method: "POST", body: { - ...globalUser, - password: body.password || undefined, - status: body.status, - email: body.email, - roles, - builder: { - global: true, - }, + roles: { + ...self.roles, + [appId]: roleId, + } }, } - const response = await fetch( checkSlashesInUrl(env.WORKER_URL + endpoint), request(ctx, reqCfg) ) const json = await response.json() if (json.status !== 200 && response.status !== 200) { - ctx.throw(400, "Unable to save global user.") - } - delete body.password - delete body.roles - delete body.builder - // TODO: for now these have been left in as they are - // TODO: pretty important to keeping relationships working - // TODO: however if user metadata is changed this should be removed - // delete body.email - // delete body.roleId - // delete body.status - return { - ...body, - _id: json._id, + ctx.throw(400, "Unable to save self globally.") } + return json } diff --git a/packages/worker/src/api/controllers/admin/roles.js b/packages/worker/src/api/controllers/admin/roles.js index 84f5ab2b71..0f6daf8aa2 100644 --- a/packages/worker/src/api/controllers/admin/roles.js +++ b/packages/worker/src/api/controllers/admin/roles.js @@ -8,7 +8,7 @@ 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 apps = await getAllApps({ dev: true }) const promises = [] for (let app of apps) { // use dev app IDs diff --git a/packages/worker/src/api/controllers/admin/users.js b/packages/worker/src/api/controllers/admin/users.js index edc15ca179..5ac9772bb1 100644 --- a/packages/worker/src/api/controllers/admin/users.js +++ b/packages/worker/src/api/controllers/admin/users.js @@ -98,7 +98,7 @@ exports.destroy = async ctx => { exports.getSelf = async ctx => { ctx.params = { - id: ctx.user._id + id: ctx.user._id, } // this will set the body await exports.find(ctx) @@ -172,12 +172,16 @@ exports.invite = async ctx => { } exports.inviteAccept = async ctx => { - const { inviteCode } = ctx.request.body + const { inviteCode, password, firstName, lastName } = ctx.request.body try { const email = await checkInviteCode(inviteCode) - // redirect the request - delete ctx.request.body.inviteCode - ctx.request.body.email = email + // only pass through certain props for accepting + ctx.request.body = { + firstName, + lastName, + password, + email, + } // this will flesh out the body response await exports.save(ctx) } catch (err) { diff --git a/packages/worker/src/api/routes/admin/configs.js b/packages/worker/src/api/routes/admin/configs.js index d1dfc5e671..09b8f97f84 100644 --- a/packages/worker/src/api/routes/admin/configs.js +++ b/packages/worker/src/api/routes/admin/configs.js @@ -78,8 +78,13 @@ function buildConfigGetValidation() { } router - .post("/api/admin/configs", buildConfigSaveValidation(), controller.save) - .delete("/api/admin/configs/:id", controller.destroy) + .post( + "/api/admin/configs", + adminOnly, + buildConfigSaveValidation(), + controller.save + ) + .delete("/api/admin/configs/:id", adminOnly, controller.destroy) .get("/api/admin/configs", controller.fetch) .get("/api/admin/configs/checklist", controller.configChecklist) .get( @@ -90,6 +95,7 @@ router .get("/api/admin/configs/:type", buildConfigGetValidation(), controller.find) .post( "/api/admin/configs/upload/:type/:name", + adminOnly, buildUploadValidation(), controller.upload ) diff --git a/packages/worker/src/api/routes/admin/groups.js b/packages/worker/src/api/routes/admin/groups.js index 6ae8780a22..4611e67079 100644 --- a/packages/worker/src/api/routes/admin/groups.js +++ b/packages/worker/src/api/routes/admin/groups.js @@ -1,6 +1,7 @@ const Router = require("@koa/router") const controller = require("../../controllers/admin/groups") const joiValidator = require("../../../middleware/joi-validator") +const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") const router = Router() @@ -24,9 +25,14 @@ function buildGroupSaveValidation() { } router - .post("/api/admin/groups", buildGroupSaveValidation(), controller.save) + .post( + "/api/admin/groups", + adminOnly, + buildGroupSaveValidation(), + controller.save + ) .get("/api/admin/groups", controller.fetch) - .delete("/api/admin/groups/:id", controller.destroy) + .delete("/api/admin/groups/:id", adminOnly, controller.destroy) .get("/api/admin/groups/:id", controller.find) module.exports = router diff --git a/packages/worker/src/api/routes/admin/roles.js b/packages/worker/src/api/routes/admin/roles.js index 3e14eb0601..2deef6b3fe 100644 --- a/packages/worker/src/api/routes/admin/roles.js +++ b/packages/worker/src/api/routes/admin/roles.js @@ -1,10 +1,11 @@ const Router = require("@koa/router") const controller = require("../../controllers/admin/roles") +const adminOnly = require("../../../middleware/adminOnly") const router = Router() router - .get("/api/admin/roles", controller.fetch) - .get("/api/admin/roles/:appId", controller.find) + .get("/api/admin/roles", adminOnly, controller.fetch) + .get("/api/admin/roles/:appId", adminOnly, controller.find) module.exports = router diff --git a/packages/worker/src/api/routes/admin/templates.js b/packages/worker/src/api/routes/admin/templates.js index a00278ccac..52ab24878b 100644 --- a/packages/worker/src/api/routes/admin/templates.js +++ b/packages/worker/src/api/routes/admin/templates.js @@ -3,6 +3,7 @@ const controller = require("../../controllers/admin/templates") const joiValidator = require("../../../middleware/joi-validator") const Joi = require("joi") const { TemplatePurpose, TemplateTypes } = require("../../../constants") +const adminOnly = require("../../../middleware/adminOnly") const router = Router() @@ -21,11 +22,16 @@ function buildTemplateSaveValidation() { router .get("/api/admin/template/definitions", controller.definitions) - .post("/api/admin/template", buildTemplateSaveValidation(), controller.save) + .post( + "/api/admin/template", + adminOnly, + buildTemplateSaveValidation(), + controller.save + ) .get("/api/admin/template", controller.fetch) .get("/api/admin/template/:type", controller.fetchByType) .get("/api/admin/template/:ownerId", controller.fetchByOwner) .get("/api/admin/template/:id", controller.find) - .delete("/api/admin/template/:id/:rev", controller.destroy) + .delete("/api/admin/template/:id/:rev", adminOnly, controller.destroy) module.exports = router diff --git a/packages/worker/src/api/routes/admin/users.js b/packages/worker/src/api/routes/admin/users.js index 2abaf91dd5..2e49c1bc40 100644 --- a/packages/worker/src/api/routes/admin/users.js +++ b/packages/worker/src/api/routes/admin/users.js @@ -1,6 +1,7 @@ const Router = require("@koa/router") const controller = require("../../controllers/admin/users") const joiValidator = require("../../../middleware/joi-validator") +const adminOnly = require("../../../middleware/adminOnly") const Joi = require("joi") const router = Router() @@ -14,12 +15,11 @@ function buildUserSaveValidation(isSelf = false) { builder: Joi.object({ global: Joi.boolean().optional(), apps: Joi.array().optional(), - }).unknown(true).optional(), - // maps appId -> roleId for the user - roles: Joi.object() - .pattern(/.*/, Joi.string()) - .required() + }) .unknown(true) + .optional(), + // maps appId -> roleId for the user + roles: Joi.object().pattern(/.*/, Joi.string()).required().unknown(true), } if (!isSelf) { schema = { @@ -28,9 +28,7 @@ function buildUserSaveValidation(isSelf = false) { _rev: Joi.string(), } } - return joiValidator.body(Joi.object(schema) - .required() - .unknown(true)) + return joiValidator.body(Joi.object(schema).required().unknown(true)) } function buildInviteValidation() { @@ -48,24 +46,30 @@ function buildInviteAcceptValidation() { }).required().unknown(true)) } -function buildUpdateSelfValidation() { - // prettier-ignore - return joiValidator.body(Joi.object({ - inviteCode: Joi.string().required(), - password: Joi.string().required(), - }).required().unknown(true)) -} - router - .post("/api/admin/users", buildUserSaveValidation(), controller.save) + .post( + "/api/admin/users", + adminOnly, + buildUserSaveValidation(), + controller.save + ) .get("/api/admin/users", controller.fetch) .post("/api/admin/users/init", controller.adminUser) .get("/api/admin/users/self", controller.getSelf) - .post("/api/admin/users/self", buildUserSaveValidation(true), controller.updateSelf) - .delete("/api/admin/users/:id", controller.destroy) + .post( + "/api/admin/users/self", + buildUserSaveValidation(true), + controller.updateSelf + ) + .delete("/api/admin/users/:id", adminOnly, controller.destroy) .get("/api/admin/users/:id", controller.find) .get("/api/admin/roles/:appId") - .post("/api/admin/users/invite", buildInviteValidation(), controller.invite) + .post( + "/api/admin/users/invite", + adminOnly, + buildInviteValidation(), + controller.invite + ) .post( "/api/admin/users/invite/accept", buildInviteAcceptValidation(), diff --git a/packages/worker/src/middleware/adminOnly.js b/packages/worker/src/middleware/adminOnly.js index 0f1a77346d..507fbda9a2 100644 --- a/packages/worker/src/middleware/adminOnly.js +++ b/packages/worker/src/middleware/adminOnly.js @@ -3,4 +3,4 @@ module.exports = async (ctx, next) => { ctx.throw(403, "Admin user only endpoint.") } return next() -} \ No newline at end of file +}