+
diff --git a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte
index d966a5a569..97100b6626 100644
--- a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte
+++ b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowList/CreateWorkflowModal.svelte
@@ -1,7 +1,6 @@
diff --git a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte
index 442647761d..f5cd0c440d 100644
--- a/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte
+++ b/packages/builder/src/components/workflow/WorkflowPanel/WorkflowPanel.svelte
@@ -2,7 +2,6 @@
import { onMount } from "svelte"
import { backendUiStore, workflowStore } from "builderStore"
import { WorkflowList, BlockList } from "./"
- import api from "builderStore/api"
import blockDefinitions from "./blockDefinitions"
let selectedTab = "WORKFLOWS"
@@ -11,6 +10,7 @@
{#if $workflowStore.currentWorkflow}
(selectedTab = 'ADD')}>
diff --git a/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte b/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte
index f1b3bae003..a00f7b4ab0 100644
--- a/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte
+++ b/packages/builder/src/pages/[application]/workflow/[workflow]/_layout.svelte
@@ -1,3 +1,4 @@
diff --git a/packages/cli/src/commands/init/initHandler.js b/packages/cli/src/commands/init/initHandler.js
index c0c4ad1b9e..3fe4134447 100644
--- a/packages/cli/src/commands/init/initHandler.js
+++ b/packages/cli/src/commands/init/initHandler.js
@@ -7,7 +7,7 @@ const Sqrl = require("squirrelly")
const uuid = require("uuid")
module.exports = opts => {
- run(opts)
+ return run(opts)
}
const run = async opts => {
diff --git a/packages/cli/src/commands/new/newHandler.js b/packages/cli/src/commands/new/newHandler.js
index 44007acde7..64c974cdf9 100644
--- a/packages/cli/src/commands/new/newHandler.js
+++ b/packages/cli/src/commands/new/newHandler.js
@@ -17,9 +17,7 @@ const run = async opts => {
exec(`cd ${join(opts.dir, opts.applicationId)} && npm install`)
console.log(chalk.green(`Budibase app ${opts.name} created!`))
} catch (error) {
- console.error(
- chalk.red("Error creating new app", JSON.stringify(error, { space: 2 }))
- )
+ console.error(chalk.red("Error creating new app", error))
}
}
@@ -53,7 +51,9 @@ const createAppInstance = async opts => {
const createInstCtx = {
params: {
clientId: process.env.CLIENT_ID,
- applicationId: opts.applicationId,
+ },
+ user: {
+ appId: opts.applicationId,
},
request: {
body: { name: `dev-${process.env.CLIENT_ID}` },
diff --git a/packages/cli/src/commands/run/runHandler.js b/packages/cli/src/commands/run/runHandler.js
index 04b21a4ff0..e3f669cf53 100644
--- a/packages/cli/src/commands/run/runHandler.js
+++ b/packages/cli/src/commands/run/runHandler.js
@@ -5,7 +5,6 @@ module.exports = ({ dir }) => {
dir = xPlatHomeDir(dir)
process.env.BUDIBASE_DIR = resolve(dir)
require("dotenv").config({ path: resolve(dir, ".env") })
- console.log("dotenv loaded")
// dont make this a variable or top level require
// ti will cause environment module to be loaded prematurely
diff --git a/packages/client/package.json b/packages/client/package.json
index 01b90a7bc5..acefff3770 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -15,7 +15,7 @@
"client": "web"
}
},
- "testURL": "http://jest-breaks-if-this-does-not-exist",
+ "testURL": "http://test.com",
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/internals/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
diff --git a/packages/client/src/api/index.js b/packages/client/src/api/index.js
index f019897494..373ac1809e 100644
--- a/packages/client/src/api/index.js
+++ b/packages/client/src/api/index.js
@@ -1,9 +1,9 @@
import { authenticate } from "./authenticate"
import { triggerWorkflow } from "./workflow"
-export const createApi = ({ rootPath = "", setState, getState }) => {
+export const createApi = ({ setState, getState }) => {
const apiCall = method => async ({ url, body }) => {
- const response = await fetch(`${rootPath}${url}`, {
+ const response = await fetch(url, {
method: method,
headers: {
"Content-Type": "application/json",
@@ -45,7 +45,6 @@ export const createApi = ({ rootPath = "", setState, getState }) => {
const isSuccess = obj => !obj || !obj[ERROR_MEMBER]
const apiOpts = {
- rootPath,
setState,
getState,
isSuccess,
diff --git a/packages/client/src/createApp.js b/packages/client/src/createApp.js
index a80c1009c7..9e1a9ea1b1 100644
--- a/packages/client/src/createApp.js
+++ b/packages/client/src/createApp.js
@@ -2,6 +2,7 @@ import { attachChildren } from "./render/attachChildren"
import { createTreeNode } from "./render/prepareRenderComponent"
import { screenRouter } from "./render/screenRouter"
import { createStateManager } from "./state/stateManager"
+import { getAppId } from "./render/getAppId"
export const createApp = ({
componentLibraries,
@@ -15,11 +16,9 @@ export const createApp = ({
const onScreenSlotRendered = screenSlotNode => {
const onScreenSelected = (screen, url) => {
const stateManager = createStateManager({
- frontendDefinition,
componentLibraries,
onScreenSlotRendered: () => {},
routeTo,
- appRootPath: frontendDefinition.appRootPath,
})
const getAttachChildrenParams = attachChildrenParams(stateManager)
screenSlotNode.props._children = [screen.props]
@@ -36,10 +35,10 @@ export const createApp = ({
routeTo = screenRouter({
screens: frontendDefinition.screens,
onScreenSelected,
- appRootPath: frontendDefinition.appRootPath,
+ window,
})
const fallbackPath = window.location.pathname.replace(
- frontendDefinition.appRootPath,
+ getAppId(window.document.cookie),
""
)
routeTo(currentUrl || fallbackPath)
@@ -59,10 +58,8 @@ export const createApp = ({
let rootTreeNode
const pageStateManager = createStateManager({
- frontendDefinition,
componentLibraries,
onScreenSlotRendered,
- appRootPath: frontendDefinition.appRootPath,
// seems weird, but the routeTo variable may not be available at this point
routeTo: url => routeTo(url),
})
diff --git a/packages/client/src/index.js b/packages/client/src/index.js
index 95f722d445..87813a812d 100644
--- a/packages/client/src/index.js
+++ b/packages/client/src/index.js
@@ -1,5 +1,6 @@
import { createApp } from "./createApp"
import { builtins, builtinLibName } from "./render/builtinComponents"
+import { getAppId } from "./render/getAppId"
/**
* create a web application from static budibase definition files.
@@ -8,7 +9,7 @@ import { builtins, builtinLibName } from "./render/builtinComponents"
export const loadBudibase = async opts => {
const _window = (opts && opts.window) || window
// const _localStorage = (opts && opts.localStorage) || localStorage
-
+ const appId = getAppId(_window.document.cookie)
const frontendDefinition = _window["##BUDIBASE_FRONTEND_DEFINITION##"]
const user = {}
@@ -20,9 +21,7 @@ export const loadBudibase = async opts => {
for (let library of libraries) {
// fetch the JavaScript for the component libraries from the server
componentLibraryModules[library] = await import(
- `/${frontendDefinition.appId}/componentlibrary?library=${encodeURI(
- library
- )}`
+ `/componentlibrary?library=${encodeURI(library)}`
)
}
@@ -38,11 +37,11 @@ export const loadBudibase = async opts => {
componentLibraries: componentLibraryModules,
frontendDefinition,
user,
- window,
+ window: _window,
})
const route = _window.location
- ? _window.location.pathname.replace(frontendDefinition.appRootPath, "")
+ ? _window.location.pathname.replace(`${appId}/`, "").replace(appId, "")
: ""
initialisePage(frontendDefinition.page, _window.document.body, route)
diff --git a/packages/client/src/render/getAppId.js b/packages/client/src/render/getAppId.js
new file mode 100644
index 0000000000..6d3df8eac2
--- /dev/null
+++ b/packages/client/src/render/getAppId.js
@@ -0,0 +1,12 @@
+export const getAppId = docCookie => {
+ const cookie =
+ docCookie.split(";").find(c => c.trim().startsWith("budibase:token")) ||
+ docCookie.split(";").find(c => c.trim().startsWith("builder:token"))
+
+ const base64Token = cookie.substring(lengthOfKey)
+
+ const user = JSON.parse(atob(base64Token.split(".")[1]))
+ return user.appId
+}
+
+const lengthOfKey = "budibase:token=".length
diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js
index 64319102f8..fb0b005096 100644
--- a/packages/client/src/render/screenRouter.js
+++ b/packages/client/src/render/screenRouter.js
@@ -1,11 +1,20 @@
import regexparam from "regexparam"
import { routerStore } from "../state/store"
+import { getAppId } from "./getAppId"
-export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => {
+export const screenRouter = ({ screens, onScreenSelected, window }) => {
const makeRootedPath = url => {
- if (appRootPath) {
- if (url) return `${appRootPath}${url.startsWith("/") ? "" : "/"}${url}`
- return appRootPath
+ if (
+ window.location &&
+ (window.location.hostname === "localhost" ||
+ window.location.hostname === "127.0.0.1")
+ ) {
+ const appId = getAppId(window.document.cookie)
+ if (url) {
+ if (url.startsWith(appId)) return url
+ return `/${appId}${url.startsWith("/") ? "" : "/"}${url}`
+ }
+ return appId
}
return url
}
@@ -70,7 +79,7 @@ export const screenRouter = ({ screens, onScreenSelected, appRootPath }) => {
)
return
- const target = x.target || "_self"
+ const target = (x && x.target) || "_self"
if (!y || target !== "_self" || x.host !== location.host) return
e.preventDefault()
diff --git a/packages/client/src/state/bbComponentApi.js b/packages/client/src/state/bbComponentApi.js
index 7366a90f5c..112e3e8534 100644
--- a/packages/client/src/state/bbComponentApi.js
+++ b/packages/client/src/state/bbComponentApi.js
@@ -6,31 +6,19 @@ export const trimSlash = str => str.replace(/^\/+|\/+$/g, "")
export const bbFactory = ({
store,
- frontendDefinition,
componentLibraries,
onScreenSlotRendered,
}) => {
- const relativeUrl = url => {
- if (!frontendDefinition.appRootPath) return url
- if (
- url.startsWith("http:") ||
- url.startsWith("https:") ||
- url.startsWith("./")
- )
- return url
-
- return frontendDefinition.appRootPath + "/" + trimSlash(url)
- }
-
- const apiCall = method => (url, body) =>
- fetch(url, {
+ const apiCall = method => (url, body) => {
+ return fetch(url, {
method: method,
headers: {
"Content-Type": "application/json",
- "x-user-agent": "Budibase Builder",
},
body: body && JSON.stringify(body),
+ credentials: "same-origin",
})
+ }
const api = {
post: apiCall("POST"),
@@ -63,7 +51,6 @@ export const bbFactory = ({
getContext: getContext(treeNode),
setContext: setContext(treeNode),
store: store,
- relativeUrl,
api,
parent,
}
diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js
index b013956dd4..cab825c24b 100644
--- a/packages/client/src/state/eventHandlers.js
+++ b/packages/client/src/state/eventHandlers.js
@@ -6,14 +6,13 @@ import { createApi } from "../api"
export const EVENT_TYPE_MEMBER_NAME = "##eventHandlerType"
-export const eventHandlers = (rootPath, routeTo) => {
+export const eventHandlers = routeTo => {
const handler = (parameters, execute) => ({
execute,
parameters,
})
const api = createApi({
- rootPath,
setState,
getState: (path, fallback) => getState(path, fallback),
})
diff --git a/packages/client/src/state/stateManager.js b/packages/client/src/state/stateManager.js
index 8e777ca4b9..5c5f198956 100644
--- a/packages/client/src/state/stateManager.js
+++ b/packages/client/src/state/stateManager.js
@@ -21,13 +21,11 @@ const isMetaProp = propName =>
propName === "_styles"
export const createStateManager = ({
- appRootPath,
- frontendDefinition,
componentLibraries,
onScreenSlotRendered,
routeTo,
}) => {
- let handlerTypes = eventHandlers(appRootPath, routeTo)
+ let handlerTypes = eventHandlers(routeTo)
let currentState
const getCurrentState = () => currentState
@@ -35,7 +33,6 @@ export const createStateManager = ({
const bb = bbFactory({
store: appStore,
getCurrentState,
- frontendDefinition,
componentLibraries,
onScreenSlotRendered,
})
diff --git a/packages/client/tests/screenRouting.spec.js b/packages/client/tests/screenRouting.spec.js
index bb4380196f..3e4818961d 100644
--- a/packages/client/tests/screenRouting.spec.js
+++ b/packages/client/tests/screenRouting.spec.js
@@ -18,7 +18,7 @@ describe("screenRouting", () => {
it("should load correct screen, for initial URL, when appRootPath is something", async () => {
const { page, screens } = pageWith3Screens()
- const { dom } = await load(page, screens, "/testApp/screen2", "/testApp")
+ const { dom } = await load(page, screens, "/TEST_APP_ID/screen2", "127.0.0.1")
const rootDiv = dom.window.document.body.children[0]
expect(rootDiv.children.length).toBe(1)
@@ -50,8 +50,8 @@ describe("screenRouting", () => {
const { dom, app } = await load(
page,
screens,
- "/testApp/screen2",
- "/testApp"
+ "/TEST_APP_ID/screen2",
+ "127.0.0.1"
)
app.routeTo()("/screen3")
diff --git a/packages/client/tests/testAppDef.js b/packages/client/tests/testAppDef.js
index c02c18a3b2..76bb3784b0 100644
--- a/packages/client/tests/testAppDef.js
+++ b/packages/client/tests/testAppDef.js
@@ -1,19 +1,33 @@
-import { JSDOM } from "jsdom"
+import jsdom, { JSDOM } from "jsdom"
import { loadBudibase } from "../src/index"
-export const load = async (page, screens, url, appRootPath) => {
+export const load = async (page, screens, url, host = "test.com") => {
screens = screens || []
url = url || "/"
- appRootPath = appRootPath || ""
+
+ const fullUrl = `http://${host}${url}`
+ const cookieJar = new jsdom.CookieJar()
+ const cookie = `${btoa("{}")}.${btoa('{"appId":"TEST_APP_ID"}')}.signature`
+ cookieJar.setCookie(
+ `budibase:token=${cookie};domain=${host};path=/`,
+ fullUrl,
+ {
+ looseMode: false,
+ },
+ () => {}
+ )
+
const dom = new JSDOM("", {
- url: `http://test${url}`,
+ url: fullUrl,
+ cookieJar,
})
+
autoAssignIds(page.props)
for (let s of screens) {
autoAssignIds(s.props)
}
setAppDef(dom.window, page, screens)
- addWindowGlobals(dom.window, page, screens, appRootPath, {
+ addWindowGlobals(dom.window, page, screens, {
hierarchy: {},
actions: [],
triggers: [],
@@ -27,11 +41,10 @@ export const load = async (page, screens, url, appRootPath) => {
return { dom, app }
}
-const addWindowGlobals = (window, page, screens, appRootPath) => {
+const addWindowGlobals = (window, page, screens) => {
window["##BUDIBASE_FRONTEND_DEFINITION##"] = {
page,
screens,
- appRootPath,
}
}
@@ -88,7 +101,6 @@ const setAppDef = (window, page, screens) => {
componentLibraries: [],
page,
screens,
- appRootPath: "",
}
}
diff --git a/packages/materialdesign-components/src/Test/createApp.js b/packages/materialdesign-components/src/Test/createApp.js
index 96106635b7..0bd3098688 100644
--- a/packages/materialdesign-components/src/Test/createApp.js
+++ b/packages/materialdesign-components/src/Test/createApp.js
@@ -14,12 +14,6 @@ export default async () => {
componentLibraries["@budibase/standard-components"] = standardcomponents
const appDef = { hierarchy: {}, actions: {} }
const user = { name: "yeo", permissions: [] }
- const { initialisePage } = createApp(
- componentLibraries,
- { appRootPath: "" },
- appDef,
- user,
- {}
- )
+ const { initialisePage } = createApp(componentLibraries, {}, appDef, user, {})
return initialisePage
}
diff --git a/packages/server/.env.template b/packages/server/.env.template
index 170d391520..f282d9c67f 100644
--- a/packages/server/.env.template
+++ b/packages/server/.env.template
@@ -5,9 +5,6 @@ COUCH_DB_URL={{couchDbUrl}}
# identifies a client database - i.e. group of apps
CLIENT_ID={{clientId}}
-# Full access API key for server
-ADMIN_SECRET={{adminSecret}}
-
# used to create cookie hashes
JWT_SECRET={{cookieKey1}}
diff --git a/packages/server/scripts/jestSetup.js b/packages/server/scripts/jestSetup.js
index ab5bc83885..8fc7082554 100644
--- a/packages/server/scripts/jestSetup.js
+++ b/packages/server/scripts/jestSetup.js
@@ -4,5 +4,4 @@ process.env.NODE_ENV = "jest"
process.env.JWT_SECRET = "test-jwtsecret"
process.env.CLIENT_ID = "test-client-id"
process.env.BUDIBASE_DIR = tmpdir("budibase-unittests")
-process.env.ADMIN_SECRET = "test-admin-secret"
process.env.LOG_LEVEL = "silent"
diff --git a/packages/server/src/api/controllers/accesslevel.js b/packages/server/src/api/controllers/accesslevel.js
index 9c9c7ff727..44b638ed41 100644
--- a/packages/server/src/api/controllers/accesslevel.js
+++ b/packages/server/src/api/controllers/accesslevel.js
@@ -8,7 +8,7 @@ const {
} = require("../../utilities/accessLevels")
exports.fetch = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const body = await db.query("database/by_type", {
include_docs: true,
key: ["accesslevel"],
@@ -19,12 +19,12 @@ exports.fetch = async function(ctx) {
{
_id: ADMIN_LEVEL_ID,
name: "Admin",
- permissions: await generateAdminPermissions(ctx.params.instanceId),
+ permissions: await generateAdminPermissions(ctx.user.instanceId),
},
{
_id: POWERUSER_LEVEL_ID,
name: "Power User",
- permissions: await generatePowerUserPermissions(ctx.params.instanceId),
+ permissions: await generatePowerUserPermissions(ctx.user.instanceId),
},
]
@@ -32,12 +32,12 @@ exports.fetch = async function(ctx) {
}
exports.find = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
ctx.body = await db.get(ctx.params.levelId)
}
exports.update = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const level = await db.get(ctx.params.levelId)
level.name = ctx.body.name
level.permissions = ctx.request.body.permissions
@@ -48,7 +48,7 @@ exports.update = async function(ctx) {
}
exports.patch = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const level = await db.get(ctx.params.levelId)
const { removedPermissions, addedPermissions, _rev } = ctx.request.body
@@ -84,7 +84,7 @@ exports.patch = async function(ctx) {
}
exports.create = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const level = {
name: ctx.request.body.name,
@@ -101,7 +101,7 @@ exports.create = async function(ctx) {
}
exports.destroy = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
await db.remove(ctx.params.levelId, ctx.params.rev)
ctx.message = `Access Level ${ctx.params.id} deleted successfully`
ctx.status = 200
diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js
index 0f604be0e7..43463fc4fd 100644
--- a/packages/server/src/api/controllers/application.js
+++ b/packages/server/src/api/controllers/application.js
@@ -1,6 +1,6 @@
const CouchDB = require("../../db")
const ClientDb = require("../../db/clientDb")
-const { getPackageForBuilder } = require("../../utilities/builder")
+const { getPackageForBuilder, buildPage } = require("../../utilities/builder")
const newid = require("../../db/newid")
const env = require("../../environment")
const instanceController = require("./instance")
@@ -9,6 +9,7 @@ const { copy, exists, readFile, writeFile } = require("fs-extra")
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const { exec } = require("child_process")
const sqrl = require("squirrelly")
+const setBuilderToken = require("../../utilities/builder/setBuilderToken")
exports.fetch = async function(ctx) {
const db = new CouchDB(ClientDb.name(getClientId(ctx)))
@@ -25,6 +26,14 @@ exports.fetchAppPackage = async function(ctx) {
const db = new CouchDB(ClientDb.name(clientId))
const application = await db.get(ctx.params.applicationId)
ctx.body = await getPackageForBuilder(ctx.config, application)
+ /*
+ instance is hardcoded now - this can only change when we move
+ pages and screens into the database
+ */
+ const devInstance = application.instances.find(
+ i => i.name === `dev-${clientId}`
+ )
+ setBuilderToken(ctx, ctx.params.applicationId, devInstance._id)
}
exports.create = async function(ctx) {
@@ -37,10 +46,12 @@ exports.create = async function(ctx) {
const appId = newid()
// insert an appId -> clientId lookup
const masterDb = new CouchDB("clientAppLookup")
+
await masterDb.put({
_id: appId,
clientId,
})
+
const db = new CouchDB(ClientDb.name(clientId))
const newApplication = {
@@ -56,18 +67,18 @@ exports.create = async function(ctx) {
description: ctx.request.body.description,
}
- const { rev } = await db.post(newApplication)
+ const { rev } = await db.put(newApplication)
newApplication._rev = rev
-
const createInstCtx = {
- params: {
- applicationId: newApplication._id,
+ user: {
+ appId: newApplication._id,
},
request: {
body: { name: `dev-${clientId}` },
},
}
await instanceController.create(createInstCtx)
+ newApplication.instances.push(createInstCtx.body)
if (ctx.isDev) {
const newAppFolder = await createEmptyAppPackage(ctx, newApplication)
@@ -100,15 +111,23 @@ const createEmptyAppPackage = async (ctx, app) => {
await updateJsonFile(join(appsFolder, app._id, "package.json"), {
name: npmFriendlyAppName(app.name),
})
- await updateJsonFile(
+
+ const mainJson = await updateJsonFile(
join(appsFolder, app._id, "pages", "main", "page.json"),
app
)
- await updateJsonFile(
+
+ await buildPage(ctx.config, app._id, "main", { page: mainJson })
+
+ const unauthenticatedJson = await updateJsonFile(
join(appsFolder, app._id, "pages", "unauthenticated", "page.json"),
app
)
+ await buildPage(ctx.config, app._id, "unauthenticated", {
+ page: unauthenticatedJson,
+ })
+
return newAppFolder
}
@@ -134,6 +153,7 @@ const updateJsonFile = async (filePath, app) => {
const json = await readFile(filePath, "utf8")
const newJson = sqrl.Render(json, app)
await writeFile(filePath, newJson, "utf8")
+ return JSON.parse(newJson)
}
const runNpmInstall = async newAppFolder => {
diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js
index 5ec3aea617..e9570e270f 100644
--- a/packages/server/src/api/controllers/auth.js
+++ b/packages/server/src/api/controllers/auth.js
@@ -4,25 +4,31 @@ const ClientDb = require("../../db/clientDb")
const bcrypt = require("../../utilities/bcrypt")
exports.authenticate = async ctx => {
+ if (!ctx.user.appId) ctx.throw(400, "No appId")
+
const { username, password } = ctx.request.body
if (!username) ctx.throw(400, "Username Required.")
if (!password) ctx.throw(400, "Password Required")
const masterDb = new CouchDB("clientAppLookup")
- const { clientId } = await masterDb.get(ctx.params.appId)
+
+ const { clientId } = await masterDb.get(ctx.user.appId)
if (!clientId) {
ctx.throw(400, "ClientId not suplied")
}
// find the instance that the user is associated with
const db = new CouchDB(ClientDb.name(clientId))
- const appId = ctx.params.appId
- const app = await db.get(appId)
+ const app = await db.get(ctx.user.appId)
const instanceId = app.userInstanceMap[username]
if (!instanceId)
- ctx.throw(500, "User is not associated with an instance of app", appId)
+ ctx.throw(
+ 500,
+ "User is not associated with an instance of app",
+ ctx.user.appId
+ )
// Check the user exists in the instance DB by username
const instanceDb = new CouchDB(instanceId)
@@ -41,16 +47,22 @@ exports.authenticate = async ctx => {
const payload = {
userId: dbUser._id,
accessLevelId: dbUser.accessLevelId,
- instanceId: instanceId,
+ appId: ctx.user.appId,
+ instanceId,
}
const token = jwt.sign(payload, ctx.config.jwtSecret, {
expiresIn: "1 day",
})
- const ONE_DAY_FROM_NOW = new Date(Date.now() + 24 * 3600)
+ const expires = new Date()
+ expires.setDate(expires.getDate() + 1)
- ctx.cookies.set("budibase:token", token, { expires: ONE_DAY_FROM_NOW })
+ ctx.cookies.set("budibase:token", token, {
+ expires,
+ path: "/",
+ httpOnly: false,
+ })
ctx.body = {
token,
diff --git a/packages/server/src/api/controllers/instance.js b/packages/server/src/api/controllers/instance.js
index 155437371f..903d3ff651 100644
--- a/packages/server/src/api/controllers/instance.js
+++ b/packages/server/src/api/controllers/instance.js
@@ -4,19 +4,19 @@ const newid = require("../../db/newid")
exports.create = async function(ctx) {
const instanceName = ctx.request.body.name
- const appShortId = ctx.params.applicationId.substring(0, 7)
+ const { appId } = ctx.user
+ const appShortId = appId.substring(0, 7)
const instanceId = `inst_${appShortId}_${newid()}`
- const { applicationId } = ctx.params
const masterDb = new CouchDB("clientAppLookup")
- const { clientId } = await masterDb.get(applicationId)
+ const { clientId } = await masterDb.get(appId)
const db = new CouchDB(instanceId)
await db.put({
_id: "_design/database",
metadata: {
clientId,
- applicationId,
+ applicationId: appId,
},
views: {
by_username: {
@@ -46,7 +46,7 @@ exports.create = async function(ctx) {
// Add the new instance under the app clientDB
const clientDb = new CouchDB(client.name(clientId))
- const budibaseApp = await clientDb.get(applicationId)
+ const budibaseApp = await clientDb.get(appId)
const instance = { _id: instanceId, name: instanceName }
budibaseApp.instances.push(instance)
await clientDb.put(budibaseApp)
diff --git a/packages/server/src/api/controllers/model.js b/packages/server/src/api/controllers/model.js
index 650342b33c..e7792195cb 100644
--- a/packages/server/src/api/controllers/model.js
+++ b/packages/server/src/api/controllers/model.js
@@ -2,7 +2,7 @@ const CouchDB = require("../../db")
const newid = require("../../db/newid")
exports.fetch = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const body = await db.query("database/by_type", {
include_docs: true,
key: ["model"],
@@ -11,13 +11,13 @@ exports.fetch = async function(ctx) {
}
exports.find = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const model = await db.get(ctx.params.id)
ctx.body = model
}
exports.create = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const newModel = {
type: "model",
...ctx.request.body,
@@ -65,7 +65,7 @@ exports.create = async function(ctx) {
exports.update = async function() {}
exports.destroy = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const modelToDelete = await db.get(ctx.params.modelId)
diff --git a/packages/server/src/api/controllers/record.js b/packages/server/src/api/controllers/record.js
index cf8eb27606..4e4af81e01 100644
--- a/packages/server/src/api/controllers/record.js
+++ b/packages/server/src/api/controllers/record.js
@@ -3,7 +3,7 @@ const validateJs = require("validate.js")
const newid = require("../../db/newid")
exports.save = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const record = ctx.request.body
record.modelId = ctx.params.modelId
@@ -46,7 +46,7 @@ exports.save = async function(ctx) {
ctx.eventEmitter &&
ctx.eventEmitter.emit(`record:save`, {
record,
- instanceId: ctx.params.instanceId,
+ instanceId: ctx.user.instanceId,
})
ctx.body = record
ctx.status = 200
@@ -54,7 +54,7 @@ exports.save = async function(ctx) {
}
exports.fetchView = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const response = await db.query(`database/${ctx.params.viewName}`, {
include_docs: true,
})
@@ -62,7 +62,7 @@ exports.fetchView = async function(ctx) {
}
exports.fetchModelRecords = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const response = await db.query(`database/all_${ctx.params.modelId}`, {
include_docs: true,
})
@@ -70,7 +70,7 @@ exports.fetchModelRecords = async function(ctx) {
}
exports.search = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const response = await db.allDocs({
include_docs: true,
...ctx.request.body,
@@ -79,7 +79,7 @@ exports.search = async function(ctx) {
}
exports.find = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const record = await db.get(ctx.params.recordId)
if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId doe not match the record's modelId")
@@ -89,7 +89,7 @@ exports.find = async function(ctx) {
}
exports.destroy = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const record = await db.get(ctx.params.recordId)
if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId doe not match the record's modelId")
@@ -101,7 +101,7 @@ exports.destroy = async function(ctx) {
exports.validate = async function(ctx) {
const errors = await validate({
- instanceId: ctx.params.instanceId,
+ instanceId: ctx.user.instanceId,
modelId: ctx.params.modelId,
record: ctx.request.body,
})
diff --git a/packages/server/src/api/controllers/static.js b/packages/server/src/api/controllers/static.js
index 557a9efe7a..67ec8e391a 100644
--- a/packages/server/src/api/controllers/static.js
+++ b/packages/server/src/api/controllers/static.js
@@ -4,11 +4,15 @@ const {
budibaseAppsDir,
budibaseTempDir,
} = require("../../utilities/budibaseDir")
-const env = require("../../environment")
+const setBuilderToken = require("../../utilities/builder/setBuilderToken")
+const { ANON_LEVEL_ID } = require("../../utilities/accessLevels")
+const jwt = require("jsonwebtoken")
exports.serveBuilder = async function(ctx) {
let builderPath = resolve(__dirname, "../../../builder")
- ctx.cookies.set("builder:token", env.ADMIN_SECRET)
+ if (ctx.file === "index.html") {
+ setBuilderToken(ctx)
+ }
await send(ctx, ctx.file, { root: ctx.devPath || builderPath })
}
@@ -20,6 +24,33 @@ exports.serveApp = async function(ctx) {
"public",
ctx.isAuthenticated ? "main" : "unauthenticated"
)
+ // only set the appId cookie for /appId .. we COULD check for valid appIds
+ // but would like to avoid that DB hit
+ const looksLikeAppId = /^[0-9a-f]{32}$/.test(ctx.params.appId)
+ if (looksLikeAppId && !ctx.isAuthenticated) {
+ const anonUser = {
+ userId: "ANON",
+ accessLevelId: ANON_LEVEL_ID,
+ appId: ctx.params.appId,
+ }
+ const anonToken = jwt.sign(anonUser, ctx.config.jwtSecret)
+ ctx.cookies.set("budibase:token", anonToken, {
+ path: "/",
+ httpOnly: false,
+ })
+ }
+
+ await send(ctx, ctx.file || "index.html", { root: ctx.devPath || appPath })
+}
+
+exports.serveAppAsset = async function(ctx) {
+ // default to homedir
+ const appPath = resolve(
+ budibaseAppsDir(),
+ ctx.user.appId,
+ "public",
+ ctx.isAuthenticated ? "main" : "unauthenticated"
+ )
await send(ctx, ctx.file, { root: ctx.devPath || appPath })
}
@@ -28,7 +59,7 @@ exports.serveComponentLibrary = async function(ctx) {
// default to homedir
let componentLibraryPath = resolve(
budibaseAppsDir(),
- ctx.params.appId,
+ ctx.user.appId,
"node_modules",
decodeURI(ctx.query.library),
"dist"
diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js
index 1b66cf1cca..71816f1c78 100644
--- a/packages/server/src/api/controllers/user.js
+++ b/packages/server/src/api/controllers/user.js
@@ -8,7 +8,7 @@ const {
} = require("../../utilities/accessLevels")
exports.fetch = async function(ctx) {
- const database = new CouchDB(ctx.params.instanceId)
+ const database = new CouchDB(ctx.user.instanceId)
const data = await database.query("database/by_type", {
include_docs: true,
key: ["user"],
@@ -18,7 +18,7 @@ exports.fetch = async function(ctx) {
}
exports.create = async function(ctx) {
- const database = new CouchDB(ctx.params.instanceId)
+ const database = new CouchDB(ctx.user.instanceId)
const appId = (await database.get("_design/database")).metadata.applicationId
const { username, password, name, accessLevelId } = ctx.request.body
@@ -50,7 +50,7 @@ exports.create = async function(ctx) {
app.userInstanceMap = {
...app.userInstanceMap,
- [username]: ctx.params.instanceId,
+ [username]: ctx.user.instanceId,
}
await db.put(app)
@@ -66,14 +66,14 @@ exports.create = async function(ctx) {
exports.update = async function() {}
exports.destroy = async function(ctx) {
- const database = new CouchDB(ctx.params.instanceId)
+ const database = new CouchDB(ctx.user.instanceId)
await database.destroy(getUserId(ctx.params.username))
ctx.message = `User ${ctx.params.username} deleted.`
ctx.status = 200
}
exports.find = async function(ctx) {
- const database = new CouchDB(ctx.params.instanceId)
+ const database = new CouchDB(ctx.user.instanceId)
const user = await database.get(getUserId(ctx.params.username))
ctx.body = {
username: user.username,
diff --git a/packages/server/src/api/controllers/view.js b/packages/server/src/api/controllers/view.js
index 9e847da358..d449ae36f9 100644
--- a/packages/server/src/api/controllers/view.js
+++ b/packages/server/src/api/controllers/view.js
@@ -3,7 +3,7 @@ const CouchDB = require("../../db")
const controller = {
query: async () => {},
fetch: async ctx => {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const designDoc = await db.get("_design/database")
const response = []
@@ -24,7 +24,7 @@ const controller = {
ctx.body = response
},
create: async ctx => {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const newView = ctx.request.body
const designDoc = await db.get("_design/database")
@@ -38,7 +38,7 @@ const controller = {
ctx.message = `View ${newView.name} created successfully.`
},
destroy: async ctx => {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
ctx.body = await db.destroy(ctx.params.userId)
},
}
diff --git a/packages/server/src/api/controllers/workflow/index.js b/packages/server/src/api/controllers/workflow/index.js
index f1dd1bcb89..5c77bbfaa6 100644
--- a/packages/server/src/api/controllers/workflow/index.js
+++ b/packages/server/src/api/controllers/workflow/index.js
@@ -2,7 +2,7 @@ const CouchDB = require("../../../db")
const newid = require("../../../db/newid")
exports.create = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const workflow = ctx.request.body
workflow._id = newid()
@@ -22,7 +22,7 @@ exports.create = async function(ctx) {
}
exports.update = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const workflow = ctx.request.body
const response = await db.put(workflow)
@@ -40,7 +40,7 @@ exports.update = async function(ctx) {
}
exports.fetch = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
const response = await db.query(`database/by_type`, {
key: ["workflow"],
include_docs: true,
@@ -69,6 +69,6 @@ exports.fetchActionScript = async function(ctx) {
}
exports.destroy = async function(ctx) {
- const db = new CouchDB(ctx.params.instanceId)
+ const db = new CouchDB(ctx.user.instanceId)
ctx.body = await db.remove(ctx.params.id, ctx.params.rev)
}
diff --git a/packages/server/src/api/routes/accesslevel.js b/packages/server/src/api/routes/accesslevel.js
index d34acab02c..af1ff80ec6 100644
--- a/packages/server/src/api/routes/accesslevel.js
+++ b/packages/server/src/api/routes/accesslevel.js
@@ -4,11 +4,11 @@ const controller = require("../controllers/accesslevel")
const router = Router()
router
- .post("/api/:instanceId/accesslevels", controller.create)
- .put("/api/:instanceId/accesslevels", controller.update)
- .get("/api/:instanceId/accesslevels", controller.fetch)
- .get("/api/:instanceId/accesslevels/:levelId", controller.find)
- .delete("/api/:instanceId/accesslevels/:levelId/:rev", controller.destroy)
- .patch("/api/:instanceId/accesslevels/:levelId", controller.patch)
+ .post("/api/accesslevels", controller.create)
+ .put("/api/accesslevels", controller.update)
+ .get("/api/accesslevels", controller.fetch)
+ .get("/api/accesslevels/:levelId", controller.find)
+ .delete("/api/accesslevels/:levelId/:rev", controller.destroy)
+ .patch("/api/accesslevels/:levelId", controller.patch)
module.exports = router
diff --git a/packages/server/src/api/routes/auth.js b/packages/server/src/api/routes/auth.js
index fa95a3a5e6..b4b68e8929 100644
--- a/packages/server/src/api/routes/auth.js
+++ b/packages/server/src/api/routes/auth.js
@@ -3,6 +3,6 @@ const controller = require("../controllers/auth")
const router = Router()
-router.post("/:appId/api/authenticate", controller.authenticate)
+router.post("/api/authenticate", controller.authenticate)
module.exports = router
diff --git a/packages/server/src/api/routes/instance.js b/packages/server/src/api/routes/instance.js
index 9b7b3db511..00fce6bb12 100644
--- a/packages/server/src/api/routes/instance.js
+++ b/packages/server/src/api/routes/instance.js
@@ -6,7 +6,7 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router()
router
- .post("/api/:applicationId/instances", authorized(BUILDER), controller.create)
+ .post("/api/instances", authorized(BUILDER), controller.create)
.delete("/api/instances/:instanceId", authorized(BUILDER), controller.destroy)
module.exports = router
diff --git a/packages/server/src/api/routes/model.js b/packages/server/src/api/routes/model.js
index f1ec46dbe5..10882aa057 100644
--- a/packages/server/src/api/routes/model.js
+++ b/packages/server/src/api/routes/model.js
@@ -1,17 +1,21 @@
const Router = require("@koa/router")
const modelController = require("../controllers/model")
const authorized = require("../../middleware/authorized")
-const { BUILDER } = require("../../utilities/accessLevels")
+const { BUILDER, READ_MODEL } = require("../../utilities/accessLevels")
const router = Router()
router
- .get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch)
- .get("/api/:instanceId/models/:id", authorized(BUILDER), modelController.find)
- .post("/api/:instanceId/models", authorized(BUILDER), modelController.create)
+ .get("/api/models", authorized(BUILDER), modelController.fetch)
+ .get(
+ "/api/models/:id",
+ authorized(READ_MODEL, ctx => ctx.params.id),
+ modelController.find
+ )
+ .post("/api/models", authorized(BUILDER), modelController.create)
// .patch("/api/:instanceId/models", controller.update)
.delete(
- "/api/:instanceId/models/:modelId/:revId",
+ "/api/models/:modelId/:revId",
authorized(BUILDER),
modelController.destroy
)
diff --git a/packages/server/src/api/routes/record.js b/packages/server/src/api/routes/record.js
index d555d3d8c8..deba513aa9 100644
--- a/packages/server/src/api/routes/record.js
+++ b/packages/server/src/api/routes/record.js
@@ -7,28 +7,28 @@ const router = Router()
router
.get(
- "/api/:instanceId/:modelId/records",
+ "/api/:modelId/records",
authorized(READ_MODEL, ctx => ctx.params.modelId),
recordController.fetchModelRecords
)
.get(
- "/api/:instanceId/:modelId/records/:recordId",
+ "/api/:modelId/records/:recordId",
authorized(READ_MODEL, ctx => ctx.params.modelId),
recordController.find
)
- .post("/api/:instanceId/records/search", recordController.search)
+ .post("/api/records/search", recordController.search)
.post(
- "/api/:instanceId/:modelId/records",
+ "/api/:modelId/records",
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.save
)
.post(
- "/api/:instanceId/:modelId/records/validate",
+ "/api/:modelId/records/validate",
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.validate
)
.delete(
- "/api/:instanceId/:modelId/records/:recordId/:revId",
+ "/api/:modelId/records/:recordId/:revId",
authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.destroy
)
diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js
index 19823aab68..60e29bf363 100644
--- a/packages/server/src/api/routes/screen.js
+++ b/packages/server/src/api/routes/screen.js
@@ -6,12 +6,8 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router()
router
- .get("/api/:instanceId/screens", authorized(BUILDER), controller.fetch)
- .post("/api/:instanceId/screens", authorized(BUILDER), controller.save)
- .delete(
- "/api/:instanceId/:screenId/:revId",
- authorized(BUILDER),
- controller.destroy
- )
+ .get("/api/screens", authorized(BUILDER), controller.fetch)
+ .post("/api/screens", authorized(BUILDER), controller.save)
+ .delete("/api/:screenId/:revId", authorized(BUILDER), controller.destroy)
module.exports = router
diff --git a/packages/server/src/api/routes/static.js b/packages/server/src/api/routes/static.js
index 5909d31d09..6fff1996fa 100644
--- a/packages/server/src/api/routes/static.js
+++ b/packages/server/src/api/routes/static.js
@@ -21,7 +21,8 @@ if (env.NODE_ENV !== "production") {
}
router
- .get("/:appId/componentlibrary", controller.serveComponentLibrary)
- .get("/:appId/:file*", controller.serveApp)
+ .get("/componentlibrary", controller.serveComponentLibrary)
+ .get("/assets/:file*", controller.serveAppAsset)
+ .get("/:appId/:path*", controller.serveApp)
module.exports = router
diff --git a/packages/server/src/api/routes/tests/accesslevel.spec.js b/packages/server/src/api/routes/tests/accesslevel.spec.js
index ef2d1575cd..81ab6a78ad 100644
--- a/packages/server/src/api/routes/tests/accesslevel.spec.js
+++ b/packages/server/src/api/routes/tests/accesslevel.spec.js
@@ -36,17 +36,17 @@ describe("/accesslevels", () => {
beforeEach(async () => {
instanceId = (await createInstance(request, appId))._id
- model = await createModel(request, instanceId)
- view = await createView(request, instanceId)
+ model = await createModel(request, appId, instanceId)
+ view = await createView(request, appId, instanceId)
})
describe("create", () => {
it("returns a success message when level is successfully created", async () => {
const res = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({ name: "user" })
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
@@ -62,17 +62,17 @@ describe("/accesslevels", () => {
it("should list custom levels, plus 2 default levels", async () => {
const createRes = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const customLevel = createRes.body
const res = await request
- .get(`/api/${instanceId}/accesslevels`)
- .set(defaultHeaders)
+ .get(`/api/accesslevels`)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
@@ -95,22 +95,22 @@ describe("/accesslevels", () => {
describe("destroy", () => {
it("should delete custom access level", async () => {
const createRes = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL } ] })
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const customLevel = createRes.body
await request
- .delete(`/api/${instanceId}/accesslevels/${customLevel._id}/${customLevel._rev}`)
- .set(defaultHeaders)
+ .delete(`/api/accesslevels/${customLevel._id}/${customLevel._rev}`)
+ .set(defaultHeaders(appId, instanceId))
.expect(200)
await request
- .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
- .set(defaultHeaders)
+ .get(`/api/accesslevels/${customLevel._id}`)
+ .set(defaultHeaders(appId, instanceId))
.expect(404)
})
})
@@ -118,27 +118,27 @@ describe("/accesslevels", () => {
describe("patch", () => {
it("should add given permissions", async () => {
const createRes = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const customLevel = createRes.body
await request
- .patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+ .patch(`/api/accesslevels/${customLevel._id}`)
.send({
_rev: customLevel._rev,
addedPermissions: [ { itemId: model._id, name: WRITE_MODEL } ]
})
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const finalRes = await request
- .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
- .set(defaultHeaders)
+ .get(`/api/accesslevels/${customLevel._id}`)
+ .set(defaultHeaders(appId, instanceId))
.expect(200)
expect(finalRes.body.permissions.length).toBe(2)
@@ -148,7 +148,7 @@ describe("/accesslevels", () => {
it("should remove given permissions", async () => {
const createRes = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({
name: "user",
permissions: [
@@ -156,25 +156,25 @@ describe("/accesslevels", () => {
{ itemId: model._id, name: WRITE_MODEL },
]
})
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const customLevel = createRes.body
await request
- .patch(`/api/${instanceId}/accesslevels/${customLevel._id}`)
+ .patch(`/api/accesslevels/${customLevel._id}`)
.send({
_rev: customLevel._rev,
removedPermissions: [ { itemId: model._id, name: WRITE_MODEL }]
})
- .set(defaultHeaders)
+ .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/)
.expect(200)
const finalRes = await request
- .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
- .set(defaultHeaders)
+ .get(`/api/accesslevels/${customLevel._id}`)
+ .set(defaultHeaders(appId, instanceId))
.expect(200)
expect(finalRes.body.permissions.length).toBe(1)
diff --git a/packages/server/src/api/routes/tests/application.spec.js b/packages/server/src/api/routes/tests/application.spec.js
index 7f64419778..d22ab02016 100644
--- a/packages/server/src/api/routes/tests/application.spec.js
+++ b/packages/server/src/api/routes/tests/application.spec.js
@@ -34,7 +34,7 @@ describe("/applications", () => {
const res = await request
.post("/api/applications")
.send({ name: "My App" })
- .set(defaultHeaders)
+ .set(defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
expect(res.res.statusMessage).toEqual("Application My App created successfully")
@@ -49,6 +49,7 @@ describe("/applications", () => {
method: "POST",
url: `/api/applications`,
instanceId: instance._id,
+ appId: otherApplication._id,
body: { name: "My App" }
})
})
@@ -63,7 +64,7 @@ describe("/applications", () => {
const res = await request
.get("/api/applications")
- .set(defaultHeaders)
+ .set(defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
@@ -77,13 +78,13 @@ describe("/applications", () => {
const blah = await request
.post("/api/applications")
.send({ name: "app2", clientId: "new_client"})
- .set(defaultHeaders)
+ .set(defaultHeaders())
.expect('Content-Type', /json/)
//.expect(200)
const client1Res = await request
.get(`/api/applications?clientId=${TEST_CLIENT_ID}`)
- .set(defaultHeaders)
+ .set(defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
@@ -92,7 +93,7 @@ describe("/applications", () => {
const client2Res = await request
.get(`/api/applications?clientId=new_client`)
- .set(defaultHeaders)
+ .set(defaultHeaders())
.expect('Content-Type', /json/)
.expect(200)
@@ -109,6 +110,7 @@ describe("/applications", () => {
method: "GET",
url: `/api/applications`,
instanceId: instance._id,
+ appId: otherApplication._id,
})
})
})
diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js
index 495b841b10..17f096ec6b 100644
--- a/packages/server/src/api/routes/tests/couchTestUtils.js
+++ b/packages/server/src/api/routes/tests/couchTestUtils.js
@@ -4,8 +4,12 @@ const supertest = require("supertest")
const app = require("../../../app")
const {
POWERUSER_LEVEL_ID,
+ ANON_LEVEL_ID,
+ BUILDER_LEVEL_ID,
generateAdminPermissions,
} = require("../../../utilities/accessLevels")
+const jwt = require("jsonwebtoken")
+const env = require("../../../environment")
const TEST_CLIENT_ID = "test-client-id"
@@ -20,13 +24,23 @@ exports.supertest = async () => {
return { request, server }
}
-exports.defaultHeaders = {
- Accept: "application/json",
- Cookie: ["builder:token=test-admin-secret"],
- "x-user-agent": "Budibase Builder",
+exports.defaultHeaders = (appId, instanceId) => {
+ const builderUser = {
+ userId: "BUILDER",
+ accessLevelId: BUILDER_LEVEL_ID,
+ appId,
+ instanceId,
+ }
+
+ const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
+
+ return {
+ Accept: "application/json",
+ Cookie: [`builder:token=${builderToken}`],
+ }
}
-exports.createModel = async (request, instanceId, model) => {
+exports.createModel = async (request, appId, instanceId, model) => {
model = model || {
name: "TestModel",
type: "model",
@@ -42,20 +56,20 @@ exports.createModel = async (request, instanceId, model) => {
}
const res = await request
- .post(`/api/${instanceId}/models`)
- .set(exports.defaultHeaders)
+ .post(`/api/models`)
+ .set(exports.defaultHeaders(appId, instanceId))
.send(model)
return res.body
}
-exports.createView = async (request, instanceId, view) => {
+exports.createView = async (request, appId, instanceId, view) => {
view = view || {
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
}
const res = await request
- .post(`/api/${instanceId}/views`)
- .set(exports.defaultHeaders)
+ .post(`/api/views`)
+ .set(exports.defaultHeaders(appId, instanceId))
.send(view)
return res.body
}
@@ -65,10 +79,10 @@ exports.createClientDatabase = async id => await create(id || TEST_CLIENT_ID)
exports.createApplication = async (request, name = "test_application") => {
const res = await request
.post("/api/applications")
- .set(exports.defaultHeaders)
.send({
name,
})
+ .set(exports.defaultHeaders())
return res.body
}
@@ -76,23 +90,24 @@ exports.destroyClientDatabase = async () => await destroy(TEST_CLIENT_ID)
exports.createInstance = async (request, appId) => {
const res = await request
- .post(`/api/${appId}/instances`)
- .set(exports.defaultHeaders)
+ .post(`/api/instances`)
.send({
- name: "test-instance",
+ name: "test-instance2",
})
+ .set(exports.defaultHeaders(appId))
return res.body
}
exports.createUser = async (
request,
+ appId,
instanceId,
username = "babs",
password = "babs_password"
) => {
const res = await request
- .post(`/api/${instanceId}/users`)
- .set(exports.defaultHeaders)
+ .post(`/api/users`)
+ .set(exports.defaultHeaders(appId, instanceId))
.send({
name: "Bill",
username,
@@ -104,6 +119,7 @@ exports.createUser = async (
const createUserWithOnePermission = async (
request,
+ appId,
instanceId,
permName,
itemId
@@ -115,17 +131,19 @@ const createUserWithOnePermission = async (
return await createUserWithPermissions(
request,
+ appId,
instanceId,
permissions,
"onePermOnlyUser"
)
}
-const createUserWithAdminPermissions = async (request, instanceId) => {
+const createUserWithAdminPermissions = async (request, appId, instanceId) => {
let permissions = await generateAdminPermissions(instanceId)
return await createUserWithPermissions(
request,
+ appId,
instanceId,
permissions,
"adminUser"
@@ -134,6 +152,7 @@ const createUserWithAdminPermissions = async (request, instanceId) => {
const createUserWithAllPermissionExceptOne = async (
request,
+ appId,
instanceId,
permName,
itemId
@@ -145,6 +164,7 @@ const createUserWithAllPermissionExceptOne = async (
return await createUserWithPermissions(
request,
+ appId,
instanceId,
permissions,
"allPermsExceptOneUser"
@@ -153,19 +173,20 @@ const createUserWithAllPermissionExceptOne = async (
const createUserWithPermissions = async (
request,
+ appId,
instanceId,
permissions,
username
) => {
const accessRes = await request
- .post(`/api/${instanceId}/accesslevels`)
+ .post(`/api/accesslevels`)
.send({ name: "TestLevel", permissions })
- .set(exports.defaultHeaders)
+ .set(exports.defaultHeaders(appId, instanceId))
const password = `password_${username}`
await request
- .post(`/api/${instanceId}/users`)
- .set(exports.defaultHeaders)
+ .post(`/api/users`)
+ .set(exports.defaultHeaders(appId, instanceId))
.send({
name: username,
username,
@@ -173,11 +194,20 @@ const createUserWithPermissions = async (
accessLevelId: accessRes.body._id,
})
- const db = new CouchDB(instanceId)
- const designDoc = await db.get("_design/database")
+ //const db = new CouchDB(instanceId)
+ //const designDoc = await db.get("_design/database")
+
+ const anonUser = {
+ userId: "ANON",
+ accessLevelId: ANON_LEVEL_ID,
+ appId: appId,
+ }
+
+ const anonToken = jwt.sign(anonUser, env.JWT_SECRET)
const loginResult = await request
- .post(`/${designDoc.metadata.applicationId}/api/authenticate`)
+ .post(`/api/authenticate`)
+ .set({ Cookie: `budibase:token=${anonToken}` })
.send({ username, password })
// returning necessary request headers
@@ -192,12 +222,14 @@ exports.testPermissionsForEndpoint = async ({
method,
url,
body,
+ appId,
instanceId,
permissionName,
itemId,
}) => {
const headers = await createUserWithOnePermission(
request,
+ appId,
instanceId,
permissionName,
itemId
@@ -209,6 +241,7 @@ exports.testPermissionsForEndpoint = async ({
const noPermsHeaders = await createUserWithAllPermissionExceptOne(
request,
+ appId,
instanceId,
permissionName,
itemId
@@ -224,9 +257,14 @@ exports.builderEndpointShouldBlockNormalUsers = async ({
method,
url,
body,
+ appId,
instanceId,
}) => {
- const headers = await createUserWithAdminPermissions(request, instanceId)
+ const headers = await createUserWithAdminPermissions(
+ request,
+ appId,
+ instanceId
+ )
await createRequest(request, method, url, body)
.set(headers)
diff --git a/packages/server/src/api/routes/tests/instance.spec.js b/packages/server/src/api/routes/tests/instance.spec.js
index 0b8bd44cb6..3bbed4d8e6 100644
--- a/packages/server/src/api/routes/tests/instance.spec.js
+++ b/packages/server/src/api/routes/tests/instance.spec.js
@@ -24,9 +24,9 @@ describe("/instances", () => {
it("returns a success message when the instance database is successfully created", async () => {
const res = await request
- .post(`/api/${TEST_APP_ID}/instances`)
+ .post(`/api/instances`)
.send({ name: "test-instance" })
- .set(defaultHeaders)
+ .set(defaultHeaders(TEST_APP_ID))
.expect('Content-Type', /json/)
.expect(200)
@@ -42,7 +42,7 @@ describe("/instances", () => {
const instance = await createInstance(request, TEST_APP_ID);
const res = await request
.delete(`/api/instances/${instance._id}`)
- .set(defaultHeaders)
+ .set(defaultHeaders(TEST_APP_ID))
.expect(200)
expect(res.res.statusMessage).toEqual(`Instance Database ${instance._id} successfully destroyed.`);
diff --git a/packages/server/src/api/routes/tests/model.spec.js b/packages/server/src/api/routes/tests/model.spec.js
index 7134245fb3..4d4e7aeb08 100644
--- a/packages/server/src/api/routes/tests/model.spec.js
+++ b/packages/server/src/api/routes/tests/model.spec.js
@@ -33,7 +33,7 @@ describe("/models", () => {
it("returns a success message when the model is successfully created", done => {
request
- .post(`/api/${instance._id}/models`)
+ .post(`/api/models`)
.send({
name: "TestModel",
key: "name",
@@ -41,7 +41,7 @@ describe("/models", () => {
name: { type: "string" }
}
})
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
.end(async (err, res) => {
@@ -55,8 +55,9 @@ describe("/models", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "POST",
- url: `/api/${instance._id}/models`,
+ url: `/api/models`,
instanceId: instance._id,
+ appId: app._id,
body: {
name: "TestModel",
key: "name",
@@ -73,13 +74,13 @@ describe("/models", () => {
beforeEach(async () => {
instance = await createInstance(request, app._id)
- testModel = await createModel(request, instance._id, testModel)
+ testModel = await createModel(request, app._id, instance._id, testModel)
});
it("returns all the models for that instance in the response body", done => {
request
- .get(`/api/${instance._id}/models`)
- .set(defaultHeaders)
+ .get(`/api/models`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
.end(async (_, res) => {
@@ -94,8 +95,9 @@ describe("/models", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "GET",
- url: `/api/${instance._id}/models`,
+ url: `/api/models`,
instanceId: instance._id,
+ appId: app._id,
})
})
});
@@ -105,7 +107,7 @@ describe("/models", () => {
beforeEach(async () => {
instance = await createInstance(request, app._id)
- testModel = await createModel(request, instance._id, testModel)
+ testModel = await createModel(request, app._id, instance._id, testModel)
});
afterEach(() => {
@@ -114,8 +116,8 @@ describe("/models", () => {
it("returns a success response when a model is deleted.", async done => {
request
- .delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
- .set(defaultHeaders)
+ .delete(`/api/models/${testModel._id}/${testModel._rev}`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
.end(async (_, res) => {
@@ -125,7 +127,7 @@ describe("/models", () => {
})
it("deletes linked references to the model after deletion", async done => {
- const linkedModel = await createModel(request, instance._id, {
+ const linkedModel = await createModel(request, app._id, instance._id, {
name: "LinkedModel",
type: "model",
key: "name",
@@ -147,8 +149,8 @@ describe("/models", () => {
})
request
- .delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
- .set(defaultHeaders)
+ .delete(`/api/models/${testModel._id}/${testModel._rev}`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
.end(async (_, res) => {
@@ -163,8 +165,9 @@ describe("/models", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "DELETE",
- url: `/api/${instance._id}/models/${testModel._id}/${testModel._rev}`,
+ url: `/api/models/${testModel._id}/${testModel._rev}`,
instanceId: instance._id,
+ appId: app._id,
})
})
diff --git a/packages/server/src/api/routes/tests/record.spec.js b/packages/server/src/api/routes/tests/record.spec.js
index 22ac67ecdc..62cf205b1c 100644
--- a/packages/server/src/api/routes/tests/record.spec.js
+++ b/packages/server/src/api/routes/tests/record.spec.js
@@ -27,7 +27,7 @@ describe("/records", () => {
beforeEach(async () => {
instance = await createInstance(request, app._id)
- model = await createModel(request, instance._id)
+ model = await createModel(request, app._id, instance._id)
record = {
name: "Test Contact",
status: "new",
@@ -39,9 +39,9 @@ describe("/records", () => {
const createRecord = async r =>
await request
- .post(`/api/${instance._id}/${model._id}/records`)
+ .post(`/api/${model._id}/records`)
.send(r || record)
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -57,14 +57,14 @@ describe("/records", () => {
const existing = rec.body
const res = await request
- .post(`/api/${instance._id}/${model._id}/records`)
+ .post(`/api/${model._id}/records`)
.send({
_id: existing._id,
_rev: existing._rev,
modelId: model._id,
name: "Updated Name",
})
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -77,8 +77,8 @@ describe("/records", () => {
const existing = rec.body
const res = await request
- .get(`/api/${instance._id}/${model._id}/records/${existing._id}`)
- .set(defaultHeaders)
+ .get(`/api/${model._id}/records/${existing._id}`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -100,8 +100,8 @@ describe("/records", () => {
await createRecord(newRecord)
const res = await request
- .get(`/api/${instance._id}/${model._id}/records`)
- .set(defaultHeaders)
+ .get(`/api/${model._id}/records`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -122,8 +122,8 @@ describe("/records", () => {
const recordIds = [record.body._id, secondRecord.body._id]
const res = await request
- .post(`/api/${instance._id}/records/search`)
- .set(defaultHeaders)
+ .post(`/api/records/search`)
+ .set(defaultHeaders(app._id, instance._id))
.send({
keys: recordIds
})
@@ -137,8 +137,8 @@ describe("/records", () => {
it("load should return 404 when record does not exist", async () => {
await createRecord()
await request
- .get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`)
- .set(defaultHeaders)
+ .get(`/api/${model._id}/records/not-a-valid-id`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(404)
})
@@ -147,9 +147,9 @@ describe("/records", () => {
describe("validate", () => {
it("should return no errors on valid record", async () => {
const result = await request
- .post(`/api/${instance._id}/${model._id}/records/validate`)
+ .post(`/api/${model._id}/records/validate`)
.send({ name: "ivan" })
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -159,9 +159,9 @@ describe("/records", () => {
it("should errors on invalid record", async () => {
const result = await request
- .post(`/api/${instance._id}/${model._id}/records/validate`)
+ .post(`/api/${model._id}/records/validate`)
.send({ name: 1 })
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js
index d950f03c3d..a17e6fe275 100644
--- a/packages/server/src/api/routes/tests/user.spec.js
+++ b/packages/server/src/api/routes/tests/user.spec.js
@@ -36,11 +36,11 @@ describe("/users", () => {
describe("fetch", () => {
it("returns a list of users from an instance db", async () => {
- await createUser(request, instance._id, "brenda", "brendas_password")
- await createUser(request, instance._id, "pam", "pam_password")
+ await createUser(request, app._id, instance._id, "brenda", "brendas_password")
+ await createUser(request, app._id, instance._id, "pam", "pam_password")
const res = await request
- .get(`/api/${instance._id}/users`)
- .set(defaultHeaders)
+ .get(`/api/users`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -50,12 +50,13 @@ describe("/users", () => {
})
it("should apply authorization to endpoint", async () => {
- await createUser(request, instance._id, "brenda", "brendas_password")
+ await createUser(request, app._id, instance._id, "brenda", "brendas_password")
await testPermissionsForEndpoint({
request,
method: "GET",
- url: `/api/${instance._id}/users`,
+ url: `/api/users`,
instanceId: instance._id,
+ appId: app._id,
permissionName: LIST_USERS,
})
})
@@ -66,8 +67,8 @@ describe("/users", () => {
it("returns a success message when a user is successfully created", async () => {
const res = await request
- .post(`/api/${instance._id}/users`)
- .set(defaultHeaders)
+ .post(`/api/users`)
+ .set(defaultHeaders(app._id, instance._id))
.send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID })
.expect(200)
.expect('Content-Type', /json/)
@@ -81,8 +82,9 @@ describe("/users", () => {
request,
method: "POST",
body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: POWERUSER_LEVEL_ID },
- url: `/api/${instance._id}/users`,
+ url: `/api/users`,
instanceId: instance._id,
+ appId: app._id,
permissionName: USER_MANAGEMENT,
})
})
diff --git a/packages/server/src/api/routes/tests/view.spec.js b/packages/server/src/api/routes/tests/view.spec.js
index b8fd47972a..5a889bed67 100644
--- a/packages/server/src/api/routes/tests/view.spec.js
+++ b/packages/server/src/api/routes/tests/view.spec.js
@@ -30,7 +30,7 @@ describe("/views", () => {
const createView = async () =>
await request
- .post(`/api/${instance._id}/views`)
+ .post(`/api/views`)
.send({
name: "TestView",
map: `function(doc) {
@@ -40,7 +40,7 @@ describe("/views", () => {
}`,
reduce: `function(keys, values) { }`
})
- .set(defaultHeaders)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -56,14 +56,14 @@ describe("/views", () => {
describe("fetch", () => {
beforeEach(async () => {
- model = await createModel(request, instance._id);
+ model = await createModel(request, app._id, instance._id);
});
it("should only return custom views", async () => {
const view = await createView()
const res = await request
- .get(`/api/${instance._id}/views`)
- .set(defaultHeaders)
+ .get(`/api/views`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.length).toBe(1)
diff --git a/packages/server/src/api/routes/tests/workflow.spec.js b/packages/server/src/api/routes/tests/workflow.spec.js
index 1f345698bf..f8b38c53ec 100644
--- a/packages/server/src/api/routes/tests/workflow.spec.js
+++ b/packages/server/src/api/routes/tests/workflow.spec.js
@@ -63,8 +63,8 @@ describe("/workflows", () => {
describe("create", () => {
it("returns a success message when the workflow is successfully created", async () => {
const res = await request
- .post(`/api/${instance._id}/workflows`)
- .set(defaultHeaders)
+ .post(`/api/workflows`)
+ .set(defaultHeaders(app._id, instance._id))
.send(TEST_WORKFLOW)
.expect('Content-Type', /json/)
.expect(200)
@@ -77,8 +77,9 @@ describe("/workflows", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "POST",
- url: `/api/${instance._id}/workflows`,
+ url: `/api/workflows`,
instanceId: instance._id,
+ appId: app._id,
body: TEST_WORKFLOW
})
})
@@ -92,8 +93,8 @@ describe("/workflows", () => {
workflow.name = "Updated Name";
const res = await request
- .put(`/api/${instance._id}/workflows`)
- .set(defaultHeaders)
+ .put(`/api/workflows`)
+ .set(defaultHeaders(app._id, instance._id))
.send(workflow)
.expect('Content-Type', /json/)
.expect(200)
@@ -107,8 +108,8 @@ describe("/workflows", () => {
it("return all the workflows for an instance", async () => {
await createWorkflow();
const res = await request
- .get(`/api/${instance._id}/workflows`)
- .set(defaultHeaders)
+ .get(`/api/workflows`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -119,8 +120,9 @@ describe("/workflows", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "GET",
- url: `/api/${instance._id}/workflows`,
+ url: `/api/workflows`,
instanceId: instance._id,
+ appId: app._id,
})
})
})
@@ -129,8 +131,8 @@ describe("/workflows", () => {
it("deletes a workflow by its ID", async () => {
await createWorkflow();
const res = await request
- .delete(`/api/${instance._id}/workflows/${workflow.id}/${workflow.rev}`)
- .set(defaultHeaders)
+ .delete(`/api/workflows/${workflow.id}/${workflow.rev}`)
+ .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/)
.expect(200)
@@ -142,8 +144,9 @@ describe("/workflows", () => {
await builderEndpointShouldBlockNormalUsers({
request,
method: "DELETE",
- url: `/api/${instance._id}/workflows/${workflow.id}/${workflow._rev}`,
+ url: `/api/workflows/${workflow.id}/${workflow._rev}`,
instanceId: instance._id,
+ appId: app._id,
})
})
})
diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js
index 20e17c7473..3c560d3f80 100644
--- a/packages/server/src/api/routes/user.js
+++ b/packages/server/src/api/routes/user.js
@@ -6,19 +6,11 @@ const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels")
const router = Router()
router
- .get("/api/:instanceId/users", authorized(LIST_USERS), controller.fetch)
- .get(
- "/api/:instanceId/users/:username",
- authorized(USER_MANAGEMENT),
- controller.find
- )
- .post(
- "/api/:instanceId/users",
- authorized(USER_MANAGEMENT),
- controller.create
- )
+ .get("/api/users", authorized(LIST_USERS), controller.fetch)
+ .get("/api/users/:username", authorized(USER_MANAGEMENT), controller.find)
+ .post("/api/users", authorized(USER_MANAGEMENT), controller.create)
.delete(
- "/api/:instanceId/users/:username",
+ "/api/users/:username",
authorized(USER_MANAGEMENT),
controller.destroy
)
diff --git a/packages/server/src/api/routes/view.js b/packages/server/src/api/routes/view.js
index 193ece1cdf..5a73eea22e 100644
--- a/packages/server/src/api/routes/view.js
+++ b/packages/server/src/api/routes/view.js
@@ -8,13 +8,13 @@ const router = Router()
router
.get(
- "/api/:instanceId/views/:viewName",
+ "/api/views/:viewName",
authorized(READ_VIEW, ctx => ctx.params.viewName),
recordController.fetchView
)
- .get("/api/:instanceId/views", authorized(BUILDER), viewController.fetch)
+ .get("/api/views", authorized(BUILDER), viewController.fetch)
// .patch("/api/:databaseId/views", controller.update);
// .delete("/api/:instanceId/views/:viewId/:revId", controller.destroy);
- .post("/api/:instanceId/views", authorized(BUILDER), viewController.create)
+ .post("/api/views", authorized(BUILDER), viewController.create)
module.exports = router
diff --git a/packages/server/src/api/routes/workflow.js b/packages/server/src/api/routes/workflow.js
index fcb1d6e182..987e18a60f 100644
--- a/packages/server/src/api/routes/workflow.js
+++ b/packages/server/src/api/routes/workflow.js
@@ -6,20 +6,16 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router()
router
- .get("/api/:instanceId/workflows", authorized(BUILDER), controller.fetch)
+ .get("/api/workflows", authorized(BUILDER), controller.fetch)
.get("/api/workflows/:id", authorized(BUILDER), controller.find)
.get(
- "/api/:instanceId/workflows/:id/:action",
+ "/api/workflows/:id/:action",
authorized(BUILDER),
controller.fetchActionScript
)
- .put("/api/:instanceId/workflows", authorized(BUILDER), controller.update)
- .post("/api/:instanceId/workflows", authorized(BUILDER), controller.create)
+ .put("/api/workflows", authorized(BUILDER), controller.update)
+ .post("/api/workflows", authorized(BUILDER), controller.create)
.post("/api/workflows/action", controller.executeAction)
- .delete(
- "/api/:instanceId/workflows/:id/:rev",
- authorized(BUILDER),
- controller.destroy
- )
+ .delete("/api/workflows/:id/:rev", authorized(BUILDER), controller.destroy)
module.exports = router
diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js
index fdf8b9874a..9bab1ed1a0 100644
--- a/packages/server/src/environment.js
+++ b/packages/server/src/environment.js
@@ -3,7 +3,6 @@ module.exports = {
NODE_ENV: process.env.NODE_ENV,
JWT_SECRET: process.env.JWT_SECRET,
BUDIBASE_DIR: process.env.BUDIBASE_DIR,
- ADMIN_SECRET: process.env.ADMIN_SECRET,
PORT: process.env.PORT,
COUCH_DB_URL: process.env.COUCH_DB_URL,
SALT_ROUNDS: process.env.SALT_ROUNDS,
diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js
index d0ce1e2f30..36e2776abe 100644
--- a/packages/server/src/middleware/authenticated.js
+++ b/packages/server/src/middleware/authenticated.js
@@ -1,10 +1,11 @@
const jwt = require("jsonwebtoken")
const STATUS_CODES = require("../utilities/statusCodes")
-const env = require("../environment")
const accessLevelController = require("../api/controllers/accesslevel")
const {
ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID,
+ BUILDER_LEVEL_ID,
+ ANON_LEVEL_ID,
} = require("../utilities/accessLevels")
module.exports = async (ctx, next) => {
@@ -15,16 +16,21 @@ module.exports = async (ctx, next) => {
const appToken = ctx.cookies.get("budibase:token")
const builderToken = ctx.cookies.get("builder:token")
- const isBuilderAgent = ctx.headers["x-user-agent"] === "Budibase Builder"
- // all admin api access should auth with buildertoken and 'Budibase Builder user agent
- const shouldAuthAsBuilder = isBuilderAgent && builderToken
-
- if (shouldAuthAsBuilder) {
- const builderTokenValid = builderToken === env.ADMIN_SECRET
-
- ctx.isAuthenticated = builderTokenValid
- ctx.isBuilder = builderTokenValid
+ if (builderToken) {
+ try {
+ const jwtPayload = jwt.verify(builderToken, ctx.config.jwtSecret)
+ ctx.isAuthenticated = jwtPayload.accessLevelId === BUILDER_LEVEL_ID
+ ctx.user = {
+ ...jwtPayload,
+ accessLevel: await getAccessLevel(
+ jwtPayload.instanceId,
+ jwtPayload.accessLevelId
+ ),
+ }
+ } catch (_) {
+ // empty: do nothing
+ }
await next()
return
@@ -46,7 +52,7 @@ module.exports = async (ctx, next) => {
jwtPayload.accessLevelId
),
}
- ctx.isAuthenticated = true
+ ctx.isAuthenticated = ctx.user.accessLevelId !== ANON_LEVEL_ID
} catch (err) {
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
}
@@ -57,7 +63,9 @@ module.exports = async (ctx, next) => {
const getAccessLevel = async (instanceId, accessLevelId) => {
if (
accessLevelId === POWERUSER_LEVEL_ID ||
- accessLevelId === ADMIN_LEVEL_ID
+ accessLevelId === ADMIN_LEVEL_ID ||
+ accessLevelId === BUILDER_LEVEL_ID ||
+ accessLevelId === ANON_LEVEL_ID
) {
return {
_id: accessLevelId,
@@ -69,6 +77,8 @@ const getAccessLevel = async (instanceId, accessLevelId) => {
const findAccessContext = {
params: {
levelId: accessLevelId,
+ },
+ user: {
instanceId,
},
}
diff --git a/packages/server/src/middleware/authorized.js b/packages/server/src/middleware/authorized.js
index b07715af36..b452d63cf5 100644
--- a/packages/server/src/middleware/authorized.js
+++ b/packages/server/src/middleware/authorized.js
@@ -2,6 +2,7 @@ const {
adminPermissions,
ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID,
+ BUILDER_LEVEL_ID,
BUILDER,
} = require("../utilities/accessLevels")
@@ -10,7 +11,11 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
ctx.throw(403, "Session not authenticated")
}
- if (ctx.isBuilder) {
+ if (!ctx.user) {
+ ctx.throw(403, "User not found")
+ }
+
+ if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) {
await next()
return
}
@@ -20,10 +25,6 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
return
}
- if (!ctx.user) {
- ctx.throw(403, "User not found")
- }
-
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
diff --git a/packages/server/src/utilities/accessLevels.js b/packages/server/src/utilities/accessLevels.js
index 9fff76e531..50ae559d07 100644
--- a/packages/server/src/utilities/accessLevels.js
+++ b/packages/server/src/utilities/accessLevels.js
@@ -5,6 +5,8 @@ const workflowController = require("../api/controllers/workflow")
// Access Level IDs
const ADMIN_LEVEL_ID = "ADMIN"
const POWERUSER_LEVEL_ID = "POWER_USER"
+const BUILDER_LEVEL_ID = "BUILDER"
+const ANON_LEVEL_ID = "ANON"
// Permissions
const READ_MODEL = "read-model"
@@ -28,7 +30,7 @@ const generateAdminPermissions = async instanceId => [
const generatePowerUserPermissions = async instanceId => {
const fetchModelsCtx = {
- params: {
+ user: {
instanceId,
},
}
@@ -36,7 +38,7 @@ const generatePowerUserPermissions = async instanceId => {
const models = fetchModelsCtx.body
const fetchViewsCtx = {
- params: {
+ user: {
instanceId,
},
}
@@ -44,7 +46,7 @@ const generatePowerUserPermissions = async instanceId => {
const views = fetchViewsCtx.body
const fetchWorkflowsCtx = {
- params: {
+ user: {
instanceId,
},
}
@@ -83,6 +85,8 @@ const generatePowerUserPermissions = async instanceId => {
module.exports = {
ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID,
+ BUILDER_LEVEL_ID,
+ ANON_LEVEL_ID,
READ_MODEL,
WRITE_MODEL,
READ_VIEW,
@@ -90,6 +94,7 @@ module.exports = {
USER_MANAGEMENT,
BUILDER,
LIST_USERS,
+ adminPermissions,
generateAdminPermissions,
generatePowerUserPermissions,
}
diff --git a/packages/server/src/utilities/builder/buildPage.js b/packages/server/src/utilities/builder/buildPage.js
index 3d44cb8072..ea0c9cce25 100644
--- a/packages/server/src/utilities/builder/buildPage.js
+++ b/packages/server/src/utilities/builder/buildPage.js
@@ -45,18 +45,16 @@ const copyClientLib = async (appPath, pageName) => {
const buildIndexHtml = async (config, appId, pageName, appPath, pkg) => {
const appPublicPath = publicPath(appPath, pageName)
- const appRootPath = rootPath(config, appId)
const stylesheetUrl = s =>
s.startsWith("http") ? s : `/${rootPath(config, appId)}/${s}`
const templateObj = {
title: pkg.page.title || "Budibase App",
- favicon: `${appRootPath}/${pkg.page.favicon || "/_shared/favicon.png"}`,
+ favicon: `${pkg.page.favicon || "/_shared/favicon.png"}`,
stylesheets: (pkg.page.stylesheets || []).map(stylesheetUrl),
screenStyles: pkg.screens.filter(s => s._css).map(s => s._css),
pageStyle: pkg.page._css,
- appRootPath,
}
const indexHtmlTemplate = await readFile(
@@ -74,7 +72,6 @@ const buildIndexHtml = async (config, appId, pageName, appPath, pkg) => {
const buildFrontendAppDefinition = async (config, appId, pageName, pkg) => {
const appPath = appPackageFolder(config, appId)
const appPublicPath = publicPath(appPath, pageName)
- const appRootPath = rootPath(config, appId)
const filename = join(appPublicPath, "clientFrontendDefinition.js")
@@ -89,7 +86,6 @@ const buildFrontendAppDefinition = async (config, appId, pageName, pkg) => {
}
const clientUiDefinition = JSON.stringify({
- appRootPath: appRootPath,
page: pkg.page,
screens: pkg.screens,
libraries: [
diff --git a/packages/server/src/utilities/builder/index.template.html b/packages/server/src/utilities/builder/index.template.html
index 80df75662b..91b33cb284 100644
--- a/packages/server/src/utilities/builder/index.template.html
+++ b/packages/server/src/utilities/builder/index.template.html
@@ -24,15 +24,15 @@
{{ /each }}
{{ each(options.screenStyles) }}
-
+
{{ /each }}
{{ if(options.pageStyle) }}
-
+
{{ /if }}
-
-
+
+
diff --git a/packages/server/src/utilities/builder/setBuilderToken.js b/packages/server/src/utilities/builder/setBuilderToken.js
new file mode 100644
index 0000000000..12622d5522
--- /dev/null
+++ b/packages/server/src/utilities/builder/setBuilderToken.js
@@ -0,0 +1,19 @@
+const { BUILDER_LEVEL_ID } = require("../accessLevels")
+const jwt = require("jsonwebtoken")
+
+module.exports = (ctx, appId, instanceId) => {
+ const builderUser = {
+ userId: "BUILDER",
+ accessLevelId: BUILDER_LEVEL_ID,
+ instanceId,
+ appId,
+ }
+
+ const token = jwt.sign(builderUser, ctx.config.jwtSecret, {
+ expiresIn: "30 days",
+ })
+
+ var expiry = new Date()
+ expiry.setDate(expiry.getDate() + 30)
+ ctx.cookies.set("builder:token", token, { expires: expiry, httpOnly: false })
+}
diff --git a/packages/standard-components/public/clientAppDefinition.js b/packages/standard-components/public/clientAppDefinition.js
index be67dcf1a8..f77f59ec03 100644
--- a/packages/standard-components/public/clientAppDefinition.js
+++ b/packages/standard-components/public/clientAppDefinition.js
@@ -98,6 +98,5 @@ window["##BUDIBASE_APPDEFINITION##"] = {
nodeId: 0,
},
componentLibraries: ["budibase-standard-components"],
- appRootPath: "/testApp2",
props: {},
}
diff --git a/packages/standard-components/src/DataChart.svelte b/packages/standard-components/src/DataChart.svelte
index 3f7aafd8a8..bccfc6314a 100644
--- a/packages/standard-components/src/DataChart.svelte
+++ b/packages/standard-components/src/DataChart.svelte
@@ -8,7 +8,6 @@
fcRoot(FusionCharts, Charts, FusionTheme)
export let _bb
- export let _instanceId
export let model
export let type = "column2d"
@@ -25,7 +24,7 @@
}
async function fetchData() {
- const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
+ const FETCH_RECORDS_URL = `/api/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
diff --git a/packages/standard-components/src/DataForm.svelte b/packages/standard-components/src/DataForm.svelte
index f199a2bd62..1510110ba9 100644
--- a/packages/standard-components/src/DataForm.svelte
+++ b/packages/standard-components/src/DataForm.svelte
@@ -2,7 +2,6 @@
import { onMount } from "svelte"
export let _bb
- export let _instanceId
export let model
let username
@@ -21,19 +20,19 @@
$: fields = Object.keys(schema)
async function fetchModel() {
- const FETCH_MODEL_URL = `/api/${_instanceId}/models/${model}`
+ const FETCH_MODEL_URL = `/api/models/${model}`
const response = await _bb.api.get(FETCH_MODEL_URL)
modelDef = await response.json()
schema = modelDef.schema
}
async function save() {
- const SAVE_RECORD_URL = `/api/${_instanceId}/${model}/records`
+ const SAVE_RECORD_URL = `/api/${model}/records`
const response = await _bb.api.post(SAVE_RECORD_URL, newModel)
const json = await response.json()
store.update(state => {
- state[model._id] = [...state[model], json]
+ state[model] = state[model] ? [...state[model], json] : [json]
return state
})
}
diff --git a/packages/standard-components/src/DataList.svelte b/packages/standard-components/src/DataList.svelte
index 1e71f706d5..9ef0b92d2c 100644
--- a/packages/standard-components/src/DataList.svelte
+++ b/packages/standard-components/src/DataList.svelte
@@ -2,7 +2,6 @@
import { onMount } from "svelte"
export let _bb
- export let _instanceId
export let model
export let layout = "list"
@@ -12,7 +11,7 @@
async function fetchData() {
if (!model || !model.length) return
- const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
+ const FETCH_RECORDS_URL = `/api/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
diff --git a/packages/standard-components/src/DataTable.svelte b/packages/standard-components/src/DataTable.svelte
index e1e32cb9f9..e6f097314c 100644
--- a/packages/standard-components/src/DataTable.svelte
+++ b/packages/standard-components/src/DataTable.svelte
@@ -3,14 +3,13 @@
export let _bb
export let onLoad
- export let _instanceId
export let model
let headers = []
let store = _bb.store
async function fetchData() {
- const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
+ const FETCH_RECORDS_URL = `/api/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
diff --git a/packages/standard-components/src/List.svelte b/packages/standard-components/src/List.svelte
index b335b71a78..8207ef47ee 100644
--- a/packages/standard-components/src/List.svelte
+++ b/packages/standard-components/src/List.svelte
@@ -2,7 +2,6 @@
import { onMount } from "svelte"
export let _bb
- export let _instanceId
export let model
export let layout = "list"
@@ -13,7 +12,7 @@
async function fetchData() {
if (!model || !model.length) return
- const FETCH_RECORDS_URL = `/api/${_instanceId}/views/all_${model}`
+ const FETCH_RECORDS_URL = `/api/views/all_${model}`
const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) {
const json = await response.json()
diff --git a/packages/standard-components/src/Login.svelte b/packages/standard-components/src/Login.svelte
index d7763911b5..41321b29ec 100644
--- a/packages/standard-components/src/Login.svelte
+++ b/packages/standard-components/src/Login.svelte
@@ -13,30 +13,17 @@
let password = ""
let loading = false
let error = false
- let _logo = ""
let _buttonClass = ""
let _inputClass = ""
$: {
- _logo = _bb.relativeUrl(logo)
_buttonClass = buttonClass || "default-button"
_inputClass = inputClass || "default-input"
}
const login = async () => {
loading = true
- const response = await fetch(_bb.relativeUrl("/api/authenticate"), {
- body: JSON.stringify({
- username,
- password,
- }),
- headers: {
- "Content-Type": "application/json",
- "x-user-agent": "Budibase Builder",
- },
- method: "POST",
- })
-
+ const response = await _bb.api.post("/api/authenticate", { username, password })
if (response.status === 200) {
const json = await response.json()
localStorage.setItem("budibase:token", json.token)
@@ -58,24 +45,12 @@
{/if}
-
-