instanceid removal

This commit is contained in:
Michael Shanks 2020-06-18 16:59:31 +01:00
parent 30f054090f
commit bf952f8677
58 changed files with 358 additions and 287 deletions

View File

@ -1,17 +1,17 @@
const apiCall = method => async (url, body) => { const apiCall = (method, instanceId) => async (url, body) => {
const headers = {
"Content-Type": "application/json",
"x-user-agent": "Budibase Builder",
}
if (instanceId) {
headers["x-budibase-instanceid"] = instanceId
}
const response = await fetch(url, { const response = await fetch(url, {
method: method, method: method,
headers: {
"Content-Type": "application/json",
"x-user-agent": "Budibase Builder",
},
body: body && JSON.stringify(body), body: body && JSON.stringify(body),
headers,
}) })
// if (response.status === 500) {
// throw new Error("Server Error");
// }
return response return response
} }
@ -21,10 +21,20 @@ export const patch = apiCall("PATCH")
export const del = apiCall("DELETE") export const del = apiCall("DELETE")
export const put = apiCall("PUT") export const put = apiCall("PUT")
export default { // usage: api(instanceId).post(...) ... will supply instance Id in header
post, const api = instanceId => ({
get, post: apiCall("POST", instanceId),
patch, get: apiCall("GET", instanceId),
delete: del, patch: apiCall("PATCH", instanceId),
put, delete: apiCall("DELETE", instanceId),
} put: apiCall("PUT", instanceId),
})
// usage: api.post(...)... will not supply instanceid in header
api.post = apiCall("POST")
api.get = apiCall("GET")
api.patch = apiCall("PATCH")
api.delete = apiCall("DELETE")
api.put = apiCall("PUT")
export default api

View File

@ -24,8 +24,8 @@ export const getBackendUiStore = () => {
store.actions = { store.actions = {
database: { database: {
select: async db => { select: async db => {
const modelsResponse = await api.get(`/api/${db._id}/models`) const modelsResponse = await api(db._id).get(`/api/models`)
const viewsResponse = await api.get(`/api/${db._id}/views`) const viewsResponse = await api(db._id).get(`/api/views`)
const models = await modelsResponse.json() const models = await modelsResponse.json()
const views = await viewsResponse.json() const views = await viewsResponse.json()
store.update(state => { store.update(state => {

View File

@ -1,5 +1,4 @@
import { values } from "lodash/fp" import { values } from "lodash/fp"
import { backendUiStore } from "builderStore"
import * as backendStoreActions from "./backend" import * as backendStoreActions from "./backend"
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
import api from "../api" import api from "../api"
@ -295,13 +294,10 @@ const addChildComponent = store => (componentToAdd, presetName) => {
const presetProps = presetName ? component.presets[presetName] : {} const presetProps = presetName ? component.presets[presetName] : {}
const instanceId = get(backendUiStore).selectedDatabase._id
const newComponent = createProps( const newComponent = createProps(
component, component,
{ {
...presetProps, ...presetProps,
_instanceId: instanceId,
}, },
state state
) )

View File

@ -4,8 +4,8 @@ import Workflow from "./Workflow"
const workflowActions = store => ({ const workflowActions = store => ({
fetch: async instanceId => { fetch: async instanceId => {
const WORKFLOWS_URL = `/api/${instanceId}/workflows` const WORKFLOWS_URL = `/api/workflows`
const workflowResponse = await api.get(WORKFLOWS_URL) const workflowResponse = await api(instanceId).get(WORKFLOWS_URL)
const json = await workflowResponse.json() const json = await workflowResponse.json()
store.update(state => { store.update(state => {
state.workflows = json state.workflows = json
@ -19,8 +19,8 @@ const workflowActions = store => ({
steps: [], steps: [],
}, },
} }
const CREATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const CREATE_WORKFLOW_URL = `/api/workflows`
const response = await api.post(CREATE_WORKFLOW_URL, workflow) const response = await api(instanceId).post(CREATE_WORKFLOW_URL, workflow)
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
state.workflows = state.workflows.concat(json.workflow) state.workflows = state.workflows.concat(json.workflow)
@ -29,8 +29,8 @@ const workflowActions = store => ({
}) })
}, },
save: async ({ instanceId, workflow }) => { save: async ({ instanceId, workflow }) => {
const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const UPDATE_WORKFLOW_URL = `/api/workflows`
const response = await api.put(UPDATE_WORKFLOW_URL, workflow) const response = await api(instanceId).put(UPDATE_WORKFLOW_URL, workflow)
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
const existingIdx = state.workflows.findIndex( const existingIdx = state.workflows.findIndex(
@ -43,8 +43,8 @@ const workflowActions = store => ({
}) })
}, },
update: async ({ instanceId, workflow }) => { update: async ({ instanceId, workflow }) => {
const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows` const UPDATE_WORKFLOW_URL = `/api/workflows`
const response = await api.put(UPDATE_WORKFLOW_URL, workflow) const response = await api(instanceId).put(UPDATE_WORKFLOW_URL, workflow)
const json = await response.json() const json = await response.json()
store.update(state => { store.update(state => {
const existingIdx = state.workflows.findIndex( const existingIdx = state.workflows.findIndex(
@ -57,8 +57,8 @@ const workflowActions = store => ({
}, },
delete: async ({ instanceId, workflow }) => { delete: async ({ instanceId, workflow }) => {
const { _id, _rev } = workflow const { _id, _rev } = workflow
const DELETE_WORKFLOW_URL = `/api/${instanceId}/workflows/${_id}/${_rev}` const DELETE_WORKFLOW_URL = `/api/workflows/${_id}/${_rev}`
await api.delete(DELETE_WORKFLOW_URL) await api(instanceId).delete(DELETE_WORKFLOW_URL)
store.update(state => { store.update(state => {
const existingIdx = state.workflows.findIndex( const existingIdx = state.workflows.findIndex(

View File

@ -1,8 +1,8 @@
import api from "builderStore/api" import api from "builderStore/api"
export async function createUser(user, instanceId) { export async function createUser(user, instanceId) {
const CREATE_USER_URL = `/api/${instanceId}/users` const CREATE_USER_URL = `/api/users`
const response = await api.post(CREATE_USER_URL, user) const response = await api(instanceId).post(CREATE_USER_URL, user)
return await response.json() return await response.json()
} }
@ -15,21 +15,21 @@ export async function createDatabase(appname, instanceName) {
} }
export async function deleteRecord(record, instanceId) { export async function deleteRecord(record, instanceId) {
const DELETE_RECORDS_URL = `/api/${instanceId}/${record._modelId}/records/${record._id}/${record._rev}` const DELETE_RECORDS_URL = `/api/${record._modelId}/records/${record._id}/${record._rev}`
const response = await api.delete(DELETE_RECORDS_URL) const response = await api(instanceId).delete(DELETE_RECORDS_URL)
return response return response
} }
export async function saveRecord(record, instanceId, modelId) { export async function saveRecord(record, instanceId, modelId) {
const SAVE_RECORDS_URL = `/api/${instanceId}/${modelId}/records` const SAVE_RECORDS_URL = `/api/${modelId}/records`
const response = await api.post(SAVE_RECORDS_URL, record) const response = await api(instanceId).post(SAVE_RECORDS_URL, record)
return await response.json() return await response.json()
} }
export async function fetchDataForView(viewName, instanceId) { export async function fetchDataForView(viewName, instanceId) {
const FETCH_RECORDS_URL = `/api/${instanceId}/views/${viewName}` const FETCH_RECORDS_URL = `/api/views/${viewName}`
const response = await api.get(FETCH_RECORDS_URL) const response = await api(instanceId).get(FETCH_RECORDS_URL)
return await response.json() return await response.json()
} }

View File

@ -27,8 +27,8 @@
function onFinishedFieldEdit() {} function onFinishedFieldEdit() {}
async function saveModel() { async function saveModel() {
const SAVE_MODEL_URL = `/api/${instanceId}/models` const SAVE_MODEL_URL = `/api/models`
const response = await api.post(SAVE_MODEL_URL, model) const response = await api(instanceId).post(SAVE_MODEL_URL, model)
const newModel = await response.json() const newModel = await response.json()
backendUiStore.actions.models.create(newModel) backendUiStore.actions.models.create(newModel)
onClosed() onClosed()

View File

@ -29,8 +29,8 @@
function deleteView() {} function deleteView() {}
async function saveView() { async function saveView() {
const SAVE_VIEW_URL = `/api/${instanceId}/views` const SAVE_VIEW_URL = `/api/views`
const response = await api.post(SAVE_VIEW_URL, view) const response = await api(instanceId).post(SAVE_VIEW_URL, view)
backendUiStore.update(state => { backendUiStore.update(state => {
state.views = [...state.views, response.view] state.views = [...state.views, response.view]
return state return state

View File

@ -7,7 +7,6 @@
CreateEditModelModal, CreateEditModelModal,
CreateEditViewModal, CreateEditViewModal,
} from "components/database/ModelDataTable/modals" } from "components/database/ModelDataTable/modals"
import api from "builderStore/api"
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")

View File

@ -52,8 +52,8 @@
} }
async function deleteModel(modelToDelete) { async function deleteModel(modelToDelete) {
const DELETE_MODEL_URL = `/api/${instanceId}/models/${node._id}/${node._rev}` const DELETE_MODEL_URL = `/api/models/${node._id}/${node._rev}`
const response = await api.delete(DELETE_MODEL_URL) const response = await api(instanceId).delete(DELETE_MODEL_URL)
backendUiStore.update(state => { backendUiStore.update(state => {
state.models = state.models.filter( state.models = state.models.filter(
model => model._id !== modelToDelete._id model => model._id !== modelToDelete._id

View File

@ -16,8 +16,8 @@
} }
async function fetchUsers() { async function fetchUsers() {
const FETCH_USERS_URL = `/api/${currentAppInfo.instanceId}/users` const FETCH_USERS_URL = `/api/users`
const response = await api.get(FETCH_USERS_URL) const response = await api(currentAppInfo.instanceId).get(FETCH_USERS_URL)
const users = await response.json() const users = await response.json()
backendUiStore.update(state => { backendUiStore.update(state => {
state.users = users state.users = users

View File

@ -108,6 +108,7 @@
selectedComponentId, selectedComponentId,
frontendDefinition, frontendDefinition,
appId: $store.appId, appId: $store.appId,
instanceId: $backendUiStore.selectedDatabase._id,
}) })
) )
} }

View File

@ -20,6 +20,7 @@ export default `<html>
font-weight: bold; font-weight: bold;
} }
</style> </style>
<script src='/assets/budibase-client.js'></script>
<script> <script>
function receiveMessage(event) { function receiveMessage(event) {
@ -44,13 +45,11 @@ export default `<html>
document.head.appendChild(styles) document.head.appendChild(styles)
styles.appendChild(document.createTextNode(data.styles)) styles.appendChild(document.createTextNode(data.styles))
document.cookie = "budibase:appid=" + data.appId
window["##BUDIBASE_FRONTEND_DEFINITION##"] = data.frontendDefinition; window["##BUDIBASE_FRONTEND_DEFINITION##"] = data.frontendDefinition;
if (clientModule) { if (window.loadBudibase) {
clientModule.loadBudibase({ window, localStorage }) loadBudibase({ window, localStorage })
} }
} }
let clientModule
let styles let styles
let selectedComponentStyle let selectedComponentStyle
@ -60,12 +59,9 @@ export default `<html>
return false; return false;
}, true) }, true)
import('/_builder/budibase-client.esm.mjs') window.addEventListener('message', receiveMessage)
.then(module => { window.dispatchEvent(new Event('bb-ready'))
clientModule = module
window.addEventListener('message', receiveMessage)
window.dispatchEvent(new Event('bb-ready'))
})
</script> </script>
</head> </head>
<body> <body>

View File

@ -1,7 +1,6 @@
<script> <script>
import { store, backendUiStore, workflowStore } from "builderStore" import { store, backendUiStore, workflowStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
import api from "builderStore/api"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"
export let onClosed export let onClosed

View File

@ -3,7 +3,6 @@
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
import api from "builderStore/api"
import WorkflowBlockSetup from "./WorkflowBlockSetup.svelte" import WorkflowBlockSetup from "./WorkflowBlockSetup.svelte"
import DeleteWorkflowModal from "./DeleteWorkflowModal.svelte" import DeleteWorkflowModal from "./DeleteWorkflowModal.svelte"

View File

@ -3,7 +3,6 @@
import { workflowStore, backendUiStore } from "builderStore" import { workflowStore, backendUiStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
import Flowchart from "./flowchart/FlowChart.svelte" import Flowchart from "./flowchart/FlowChart.svelte"
import api from "builderStore/api"
let selectedWorkflow let selectedWorkflow
let uiTree let uiTree

View File

@ -3,7 +3,6 @@
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import { WorkflowList } from "../" import { WorkflowList } from "../"
import WorkflowBlock from "./WorkflowBlock.svelte" import WorkflowBlock from "./WorkflowBlock.svelte"
import api from "builderStore/api"
import blockDefinitions from "../blockDefinitions" import blockDefinitions from "../blockDefinitions"
let selectedTab = "TRIGGER" let selectedTab = "TRIGGER"

View File

@ -1,7 +1,6 @@
<script> <script>
import { store, backendUiStore, workflowStore } from "builderStore" import { store, backendUiStore, workflowStore } from "builderStore"
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
import api from "builderStore/api"
import ActionButton from "components/common/ActionButton.svelte" import ActionButton from "components/common/ActionButton.svelte"
export let onClosed export let onClosed

View File

@ -3,7 +3,6 @@
import { notifier } from "@beyonk/svelte-notifications" import { notifier } from "@beyonk/svelte-notifications"
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import api from "builderStore/api"
import CreateWorkflowModal from "./CreateWorkflowModal.svelte" import CreateWorkflowModal from "./CreateWorkflowModal.svelte"
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")

View File

@ -2,7 +2,6 @@
import { onMount } from "svelte" import { onMount } from "svelte"
import { backendUiStore, workflowStore } from "builderStore" import { backendUiStore, workflowStore } from "builderStore"
import { WorkflowList, BlockList } from "./" import { WorkflowList, BlockList } from "./"
import api from "builderStore/api"
import blockDefinitions from "./blockDefinitions" import blockDefinitions from "./blockDefinitions"
let selectedTab = "WORKFLOWS" let selectedTab = "WORKFLOWS"

View File

@ -1,5 +1,9 @@
export const getAppId = cookie => export const getAppId = cookie => {
cookie const base64Token = cookie
.split(";") .split(";")
.find(c => c.trim().startsWith("budibase:appid")) .find(c => c.trim().startsWith("budibase:token"))
.split("=")[1] .split("=")[1]
const user = JSON.parse(atob(base64Token.split(".")[1]))
return user.appId
}

View File

@ -7,8 +7,9 @@ export const load = async (page, screens, url, host = "test.com") => {
const fullUrl = `http://${host}${url}` const fullUrl = `http://${host}${url}`
const cookieJar = new jsdom.CookieJar() const cookieJar = new jsdom.CookieJar()
const cookie = btoa('{}{"appId":"TEST_APP_ID"}signature')
cookieJar.setCookie( cookieJar.setCookie(
`budibase:appid=TEST_APP_ID;domain=${host};path=/`, `budibase:token=${cookie};domain=${host};path=/`,
fullUrl, fullUrl,
{ {
looseMode: false, looseMode: false,
@ -20,9 +21,6 @@ export const load = async (page, screens, url, host = "test.com") => {
url: fullUrl, url: fullUrl,
cookieJar, cookieJar,
}) })
/*const cookie = tough.Cookie
cookie.key = "budibase:appid"
cookie.value = "TEST_APPID"*/
autoAssignIds(page.props) autoAssignIds(page.props)
for (let s of screens) { for (let s of screens) {
@ -44,7 +42,6 @@ export const load = async (page, screens, url, host = "test.com") => {
} }
const addWindowGlobals = (window, page, screens) => { const addWindowGlobals = (window, page, screens) => {
window.document.cookie = "budibase:appid=TEST_APP_ID"
window["##BUDIBASE_FRONTEND_DEFINITION##"] = { window["##BUDIBASE_FRONTEND_DEFINITION##"] = {
page, page,
screens, screens,

View File

@ -8,7 +8,7 @@ const {
} = require("../../utilities/accessLevels") } = require("../../utilities/accessLevels")
exports.fetch = async function(ctx) { 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", { const body = await db.query("database/by_type", {
include_docs: true, include_docs: true,
key: ["accesslevel"], key: ["accesslevel"],
@ -19,12 +19,12 @@ exports.fetch = async function(ctx) {
{ {
_id: ADMIN_LEVEL_ID, _id: ADMIN_LEVEL_ID,
name: "Admin", name: "Admin",
permissions: await generateAdminPermissions(ctx.params.instanceId), permissions: await generateAdminPermissions(ctx.user.instanceId),
}, },
{ {
_id: POWERUSER_LEVEL_ID, _id: POWERUSER_LEVEL_ID,
name: "Power User", 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) { 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) ctx.body = await db.get(ctx.params.levelId)
} }
exports.update = async function(ctx) { 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) const level = await db.get(ctx.params.levelId)
level.name = ctx.body.name level.name = ctx.body.name
level.permissions = ctx.request.body.permissions level.permissions = ctx.request.body.permissions
@ -48,7 +48,7 @@ exports.update = async function(ctx) {
} }
exports.patch = 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 level = await db.get(ctx.params.levelId)
const { removedPermissions, addedPermissions, _rev } = ctx.request.body const { removedPermissions, addedPermissions, _rev } = ctx.request.body
@ -84,7 +84,7 @@ exports.patch = async function(ctx) {
} }
exports.create = async function(ctx) { exports.create = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.user.instanceId)
const level = { const level = {
name: ctx.request.body.name, name: ctx.request.body.name,
@ -101,7 +101,7 @@ exports.create = async function(ctx) {
} }
exports.destroy = 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) await db.remove(ctx.params.levelId, ctx.params.rev)
ctx.message = `Access Level ${ctx.params.id} deleted successfully` ctx.message = `Access Level ${ctx.params.id} deleted successfully`
ctx.status = 200 ctx.status = 200

View File

@ -9,6 +9,7 @@ const { copy, exists, readFile, writeFile } = require("fs-extra")
const { budibaseAppsDir } = require("../../utilities/budibaseDir") const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const { exec } = require("child_process") const { exec } = require("child_process")
const sqrl = require("squirrelly") const sqrl = require("squirrelly")
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {
const db = new CouchDB(ClientDb.name(getClientId(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 db = new CouchDB(ClientDb.name(clientId))
const application = await db.get(ctx.params.applicationId) const application = await db.get(ctx.params.applicationId)
ctx.body = await getPackageForBuilder(ctx.config, application) 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) { exports.create = async function(ctx) {
@ -37,10 +46,12 @@ exports.create = async function(ctx) {
const appId = newid() const appId = newid()
// insert an appId -> clientId lookup // insert an appId -> clientId lookup
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("clientAppLookup")
await masterDb.put({ await masterDb.put({
_id: appId, _id: appId,
clientId, clientId,
}) })
const db = new CouchDB(ClientDb.name(clientId)) const db = new CouchDB(ClientDb.name(clientId))
const newApplication = { const newApplication = {
@ -56,18 +67,18 @@ exports.create = async function(ctx) {
description: ctx.request.body.description, description: ctx.request.body.description,
} }
const { rev } = await db.post(newApplication) const { rev } = await db.put(newApplication)
newApplication._rev = rev newApplication._rev = rev
const createInstCtx = { const createInstCtx = {
params: { user: {
applicationId: newApplication._id, appId: newApplication._id,
}, },
request: { request: {
body: { name: `dev-${clientId}` }, body: { name: `dev-${clientId}` },
}, },
} }
await instanceController.create(createInstCtx) await instanceController.create(createInstCtx)
newApplication.instances.push(createInstCtx.body)
if (ctx.isDev) { if (ctx.isDev) {
const newAppFolder = await createEmptyAppPackage(ctx, newApplication) const newAppFolder = await createEmptyAppPackage(ctx, newApplication)

View File

@ -4,7 +4,7 @@ const ClientDb = require("../../db/clientDb")
const bcrypt = require("../../utilities/bcrypt") const bcrypt = require("../../utilities/bcrypt")
exports.authenticate = async ctx => { exports.authenticate = async ctx => {
if (!ctx.appId) ctx.throw(400, "No appId") if (!ctx.user.appId) ctx.throw(400, "No appId")
const { username, password } = ctx.request.body const { username, password } = ctx.request.body
@ -13,18 +13,22 @@ exports.authenticate = async ctx => {
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("clientAppLookup")
const { clientId } = await masterDb.get(ctx.appId) const { clientId } = await masterDb.get(ctx.user.appId)
if (!clientId) { if (!clientId) {
ctx.throw(400, "ClientId not suplied") ctx.throw(400, "ClientId not suplied")
} }
// find the instance that the user is associated with // find the instance that the user is associated with
const db = new CouchDB(ClientDb.name(clientId)) const db = new CouchDB(ClientDb.name(clientId))
const app = await db.get(ctx.appId) const app = await db.get(ctx.user.appId)
const instanceId = app.userInstanceMap[username] const instanceId = app.userInstanceMap[username]
if (!instanceId) if (!instanceId)
ctx.throw(500, "User is not associated with an instance of app", ctx.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 // Check the user exists in the instance DB by username
const instanceDb = new CouchDB(instanceId) const instanceDb = new CouchDB(instanceId)
@ -43,6 +47,7 @@ exports.authenticate = async ctx => {
const payload = { const payload = {
userId: dbUser._id, userId: dbUser._id,
accessLevelId: dbUser.accessLevelId, accessLevelId: dbUser.accessLevelId,
appId: ctx.user.appId,
instanceId, instanceId,
} }

View File

@ -4,19 +4,19 @@ const newid = require("../../db/newid")
exports.create = async function(ctx) { exports.create = async function(ctx) {
const instanceName = ctx.request.body.name 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 instanceId = `inst_${appShortId}_${newid()}`
const { applicationId } = ctx.params
const masterDb = new CouchDB("clientAppLookup") const masterDb = new CouchDB("clientAppLookup")
const { clientId } = await masterDb.get(applicationId) const { clientId } = await masterDb.get(appId)
const db = new CouchDB(instanceId) const db = new CouchDB(instanceId)
await db.put({ await db.put({
_id: "_design/database", _id: "_design/database",
metadata: { metadata: {
clientId, clientId,
applicationId, applicationId: appId,
}, },
views: { views: {
by_username: { by_username: {
@ -46,7 +46,7 @@ exports.create = async function(ctx) {
// Add the new instance under the app clientDB // Add the new instance under the app clientDB
const clientDb = new CouchDB(client.name(clientId)) 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 } const instance = { _id: instanceId, name: instanceName }
budibaseApp.instances.push(instance) budibaseApp.instances.push(instance)
await clientDb.put(budibaseApp) await clientDb.put(budibaseApp)

View File

@ -2,7 +2,7 @@ const CouchDB = require("../../db")
const newid = require("../../db/newid") const newid = require("../../db/newid")
exports.fetch = async function(ctx) { 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", { const body = await db.query("database/by_type", {
include_docs: true, include_docs: true,
key: ["model"], key: ["model"],
@ -11,13 +11,13 @@ exports.fetch = async function(ctx) {
} }
exports.find = 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) const model = await db.get(ctx.params.id)
ctx.body = model ctx.body = model
} }
exports.create = async function(ctx) { exports.create = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.user.instanceId)
const newModel = { const newModel = {
type: "model", type: "model",
...ctx.request.body, ...ctx.request.body,
@ -65,7 +65,7 @@ exports.create = async function(ctx) {
exports.update = async function() {} exports.update = async function() {}
exports.destroy = async function(ctx) { 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) const modelToDelete = await db.get(ctx.params.modelId)

View File

@ -3,7 +3,7 @@ const validateJs = require("validate.js")
const newid = require("../../db/newid") const newid = require("../../db/newid")
exports.save = async function(ctx) { exports.save = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.user.instanceId)
const record = ctx.request.body const record = ctx.request.body
record.modelId = ctx.params.modelId record.modelId = ctx.params.modelId
@ -46,7 +46,7 @@ exports.save = async function(ctx) {
ctx.eventEmitter && ctx.eventEmitter &&
ctx.eventEmitter.emit(`record:save`, { ctx.eventEmitter.emit(`record:save`, {
record, record,
instanceId: ctx.params.instanceId, instanceId: ctx.user.instanceId,
}) })
ctx.body = record ctx.body = record
ctx.status = 200 ctx.status = 200
@ -54,7 +54,7 @@ exports.save = async function(ctx) {
} }
exports.fetchView = 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}`, { const response = await db.query(`database/${ctx.params.viewName}`, {
include_docs: true, include_docs: true,
}) })
@ -62,7 +62,7 @@ exports.fetchView = async function(ctx) {
} }
exports.fetchModelRecords = 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}`, { const response = await db.query(`database/all_${ctx.params.modelId}`, {
include_docs: true, include_docs: true,
}) })
@ -70,7 +70,7 @@ exports.fetchModelRecords = async function(ctx) {
} }
exports.search = 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({ const response = await db.allDocs({
include_docs: true, include_docs: true,
...ctx.request.body, ...ctx.request.body,
@ -79,7 +79,7 @@ exports.search = async function(ctx) {
} }
exports.find = 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) const record = await db.get(ctx.params.recordId)
if (record.modelId !== ctx.params.modelId) { if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId doe not match the record's 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) { 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) const record = await db.get(ctx.params.recordId)
if (record.modelId !== ctx.params.modelId) { if (record.modelId !== ctx.params.modelId) {
ctx.throw(400, "Supplied modelId doe not match the record's 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) { exports.validate = async function(ctx) {
const errors = await validate({ const errors = await validate({
instanceId: ctx.params.instanceId, instanceId: ctx.user.instanceId,
modelId: ctx.params.modelId, modelId: ctx.params.modelId,
record: ctx.request.body, record: ctx.request.body,
}) })

View File

@ -4,11 +4,12 @@ const {
budibaseAppsDir, budibaseAppsDir,
budibaseTempDir, budibaseTempDir,
} = require("../../utilities/budibaseDir") } = require("../../utilities/budibaseDir")
const env = require("../../environment") const setBuilderToken = require("../../utilities/builder/setBuilderToken")
const { ANON_LEVEL_ID } = require("../../utilities/accessLevels")
exports.serveBuilder = async function(ctx) { exports.serveBuilder = async function(ctx) {
let builderPath = resolve(__dirname, "../../../builder") let builderPath = resolve(__dirname, "../../../builder")
ctx.cookies.set("builder:token", env.ADMIN_SECRET) setBuilderToken(ctx)
await send(ctx, ctx.file, { root: ctx.devPath || builderPath }) await send(ctx, ctx.file, { root: ctx.devPath || builderPath })
} }
@ -22,11 +23,15 @@ exports.serveApp = async function(ctx) {
) )
// only set the appId cookie for /appId .. we COULD check for valid appIds // only set the appId cookie for /appId .. we COULD check for valid appIds
// but would like to avoid that DB hit // but would like to avoid that DB hit
if (looksLikeAppId(ctx.params.appId)) { if (looksLikeAppId(ctx.params.appId) && !ctx.isAuthenticated) {
ctx.cookies.set("budibase:appid", ctx.params.appId, { const anonToken = {
userId: "ANON",
accessLevelId: ANON_LEVEL_ID,
appId: ctx.params.appId,
}
ctx.cookies.set("budibase:token", anonToken, {
path: "/", path: "/",
httpOnly: false, httpOnly: false,
expires: new Date(2099, 1, 1),
}) })
} }
@ -37,7 +42,7 @@ exports.serveAppAsset = async function(ctx) {
// default to homedir // default to homedir
const appPath = resolve( const appPath = resolve(
budibaseAppsDir(), budibaseAppsDir(),
ctx.appId, ctx.user.appId,
"public", "public",
ctx.isAuthenticated ? "main" : "unauthenticated" ctx.isAuthenticated ? "main" : "unauthenticated"
) )
@ -49,7 +54,7 @@ exports.serveComponentLibrary = async function(ctx) {
// default to homedir // default to homedir
let componentLibraryPath = resolve( let componentLibraryPath = resolve(
budibaseAppsDir(), budibaseAppsDir(),
ctx.appId, ctx.user.appId,
"node_modules", "node_modules",
decodeURI(ctx.query.library), decodeURI(ctx.query.library),
"dist" "dist"

View File

@ -8,7 +8,7 @@ const {
} = require("../../utilities/accessLevels") } = require("../../utilities/accessLevels")
exports.fetch = async function(ctx) { 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", { const data = await database.query("database/by_type", {
include_docs: true, include_docs: true,
key: ["user"], key: ["user"],
@ -18,7 +18,7 @@ exports.fetch = async function(ctx) {
} }
exports.create = 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 appId = (await database.get("_design/database")).metadata.applicationId
const { username, password, name, accessLevelId } = ctx.request.body const { username, password, name, accessLevelId } = ctx.request.body
@ -50,7 +50,7 @@ exports.create = async function(ctx) {
app.userInstanceMap = { app.userInstanceMap = {
...app.userInstanceMap, ...app.userInstanceMap,
[username]: ctx.params.instanceId, [username]: ctx.user.instanceId,
} }
await db.put(app) await db.put(app)
@ -66,14 +66,14 @@ exports.create = async function(ctx) {
exports.update = async function() {} exports.update = async function() {}
exports.destroy = async function(ctx) { 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)) await database.destroy(getUserId(ctx.params.username))
ctx.message = `User ${ctx.params.username} deleted.` ctx.message = `User ${ctx.params.username} deleted.`
ctx.status = 200 ctx.status = 200
} }
exports.find = async function(ctx) { 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)) const user = await database.get(getUserId(ctx.params.username))
ctx.body = { ctx.body = {
username: user.username, username: user.username,

View File

@ -3,7 +3,7 @@ const CouchDB = require("../../db")
const controller = { const controller = {
query: async () => {}, query: async () => {},
fetch: async ctx => { 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 designDoc = await db.get("_design/database")
const response = [] const response = []
@ -24,7 +24,7 @@ const controller = {
ctx.body = response ctx.body = response
}, },
create: async ctx => { create: async ctx => {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.user.instanceId)
const newView = ctx.request.body const newView = ctx.request.body
const designDoc = await db.get("_design/database") const designDoc = await db.get("_design/database")
@ -38,7 +38,7 @@ const controller = {
ctx.message = `View ${newView.name} created successfully.` ctx.message = `View ${newView.name} created successfully.`
}, },
destroy: async ctx => { 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) ctx.body = await db.destroy(ctx.params.userId)
}, },
} }

View File

@ -2,7 +2,7 @@ const CouchDB = require("../../../db")
const newid = require("../../../db/newid") const newid = require("../../../db/newid")
exports.create = async function(ctx) { exports.create = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.user.instanceId)
const workflow = ctx.request.body const workflow = ctx.request.body
workflow._id = newid() workflow._id = newid()
@ -22,7 +22,7 @@ exports.create = async function(ctx) {
} }
exports.update = 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 workflow = ctx.request.body
const response = await db.put(workflow) const response = await db.put(workflow)
@ -40,7 +40,7 @@ exports.update = async function(ctx) {
} }
exports.fetch = 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`, { const response = await db.query(`database/by_type`, {
key: ["workflow"], key: ["workflow"],
include_docs: true, include_docs: true,
@ -69,6 +69,6 @@ exports.fetchActionScript = async function(ctx) {
} }
exports.destroy = 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) ctx.body = await db.remove(ctx.params.id, ctx.params.rev)
} }

View File

@ -60,11 +60,6 @@ router.use(async (ctx, next) => {
} }
}) })
router.use(async (ctx, next) => {
ctx.appId = ctx.cookies.get("budibase:appid")
await next()
})
router.use(authRoutes.routes()) router.use(authRoutes.routes())
router.use(authRoutes.allowedMethods()) router.use(authRoutes.allowedMethods())

View File

@ -4,11 +4,11 @@ const controller = require("../controllers/accesslevel")
const router = Router() const router = Router()
router router
.post("/api/:instanceId/accesslevels", controller.create) .post("/api/accesslevels", controller.create)
.put("/api/:instanceId/accesslevels", controller.update) .put("/api/accesslevels", controller.update)
.get("/api/:instanceId/accesslevels", controller.fetch) .get("/api/accesslevels", controller.fetch)
.get("/api/:instanceId/accesslevels/:levelId", controller.find) .get("/api/accesslevels/:levelId", controller.find)
.delete("/api/:instanceId/accesslevels/:levelId/:rev", controller.destroy) .delete("/api/accesslevels/:levelId/:rev", controller.destroy)
.patch("/api/:instanceId/accesslevels/:levelId", controller.patch) .patch("/api/accesslevels/:levelId", controller.patch)
module.exports = router module.exports = router

View File

@ -6,7 +6,7 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router() const router = 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) .delete("/api/instances/:instanceId", authorized(BUILDER), controller.destroy)
module.exports = router module.exports = router

View File

@ -1,17 +1,21 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const modelController = require("../controllers/model") const modelController = require("../controllers/model")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER, READ_MODEL } = require("../../utilities/accessLevels")
const router = Router() const router = Router()
router router
.get("/api/:instanceId/models", authorized(BUILDER), modelController.fetch) .get("/api/models", modelController.fetch)
.get("/api/:instanceId/models/:id", authorized(BUILDER), modelController.find) .get(
.post("/api/:instanceId/models", authorized(BUILDER), modelController.create) "/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) // .patch("/api/:instanceId/models", controller.update)
.delete( .delete(
"/api/:instanceId/models/:modelId/:revId", "/api/models/:modelId/:revId",
authorized(BUILDER), authorized(BUILDER),
modelController.destroy modelController.destroy
) )

View File

@ -7,28 +7,28 @@ const router = Router()
router router
.get( .get(
"/api/:instanceId/:modelId/records", "/api/:modelId/records",
authorized(READ_MODEL, ctx => ctx.params.modelId), authorized(READ_MODEL, ctx => ctx.params.modelId),
recordController.fetchModelRecords recordController.fetchModelRecords
) )
.get( .get(
"/api/:instanceId/:modelId/records/:recordId", "/api/:modelId/records/:recordId",
authorized(READ_MODEL, ctx => ctx.params.modelId), authorized(READ_MODEL, ctx => ctx.params.modelId),
recordController.find recordController.find
) )
.post("/api/:instanceId/records/search", recordController.search) .post("/api/records/search", recordController.search)
.post( .post(
"/api/:instanceId/:modelId/records", "/api/:modelId/records",
authorized(WRITE_MODEL, ctx => ctx.params.modelId), authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.save recordController.save
) )
.post( .post(
"/api/:instanceId/:modelId/records/validate", "/api/:modelId/records/validate",
authorized(WRITE_MODEL, ctx => ctx.params.modelId), authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.validate recordController.validate
) )
.delete( .delete(
"/api/:instanceId/:modelId/records/:recordId/:revId", "/api/:modelId/records/:recordId/:revId",
authorized(WRITE_MODEL, ctx => ctx.params.modelId), authorized(WRITE_MODEL, ctx => ctx.params.modelId),
recordController.destroy recordController.destroy
) )

View File

@ -6,12 +6,8 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router() const router = Router()
router router
.get("/api/:instanceId/screens", authorized(BUILDER), controller.fetch) .get("/api/screens", authorized(BUILDER), controller.fetch)
.post("/api/:instanceId/screens", authorized(BUILDER), controller.save) .post("/api/screens", authorized(BUILDER), controller.save)
.delete( .delete("/api/:screenId/:revId", authorized(BUILDER), controller.destroy)
"/api/:instanceId/:screenId/:revId",
authorized(BUILDER),
controller.destroy
)
module.exports = router module.exports = router

View File

@ -36,8 +36,8 @@ describe("/accesslevels", () => {
beforeEach(async () => { beforeEach(async () => {
instanceId = (await createInstance(request, appId))._id instanceId = (await createInstance(request, appId))._id
model = await createModel(request, instanceId) model = await createModel(request, appId, instanceId)
view = await createView(request, instanceId) view = await createView(request, appId, instanceId)
}) })
describe("create", () => { describe("create", () => {
@ -46,7 +46,7 @@ describe("/accesslevels", () => {
const res = await request const res = await request
.post(`/api/${instanceId}/accesslevels`) .post(`/api/${instanceId}/accesslevels`)
.send({ name: "user" }) .send({ name: "user" })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -64,7 +64,7 @@ describe("/accesslevels", () => {
const createRes = await request const createRes = await request
.post(`/api/${instanceId}/accesslevels`) .post(`/api/${instanceId}/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] }) .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -72,7 +72,7 @@ describe("/accesslevels", () => {
const res = await request const res = await request
.get(`/api/${instanceId}/accesslevels`) .get(`/api/${instanceId}/accesslevels`)
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -97,7 +97,7 @@ describe("/accesslevels", () => {
const createRes = await request const createRes = await request
.post(`/api/${instanceId}/accesslevels`) .post(`/api/${instanceId}/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL } ] }) .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL } ] })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -105,12 +105,12 @@ describe("/accesslevels", () => {
await request await request
.delete(`/api/${instanceId}/accesslevels/${customLevel._id}/${customLevel._rev}`) .delete(`/api/${instanceId}/accesslevels/${customLevel._id}/${customLevel._rev}`)
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect(200) .expect(200)
await request await request
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`) .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect(404) .expect(404)
}) })
}) })
@ -120,7 +120,7 @@ describe("/accesslevels", () => {
const createRes = await request const createRes = await request
.post(`/api/${instanceId}/accesslevels`) .post(`/api/${instanceId}/accesslevels`)
.send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] }) .send({ name: "user", permissions: [ { itemId: model._id, name: READ_MODEL }] })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -132,13 +132,13 @@ describe("/accesslevels", () => {
_rev: customLevel._rev, _rev: customLevel._rev,
addedPermissions: [ { itemId: model._id, name: WRITE_MODEL } ] addedPermissions: [ { itemId: model._id, name: WRITE_MODEL } ]
}) })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
const finalRes = await request const finalRes = await request
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`) .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect(200) .expect(200)
expect(finalRes.body.permissions.length).toBe(2) expect(finalRes.body.permissions.length).toBe(2)
@ -156,7 +156,7 @@ describe("/accesslevels", () => {
{ itemId: model._id, name: WRITE_MODEL }, { itemId: model._id, name: WRITE_MODEL },
] ]
}) })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -168,13 +168,13 @@ describe("/accesslevels", () => {
_rev: customLevel._rev, _rev: customLevel._rev,
removedPermissions: [ { itemId: model._id, name: WRITE_MODEL }] removedPermissions: [ { itemId: model._id, name: WRITE_MODEL }]
}) })
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
const finalRes = await request const finalRes = await request
.get(`/api/${instanceId}/accesslevels/${customLevel._id}`) .get(`/api/${instanceId}/accesslevels/${customLevel._id}`)
.set(defaultHeaders) .set(defaultHeaders(appId, instanceId))
.expect(200) .expect(200)
expect(finalRes.body.permissions.length).toBe(1) expect(finalRes.body.permissions.length).toBe(1)

View File

@ -34,14 +34,14 @@ describe("/applications", () => {
const res = await request const res = await request
.post("/api/applications") .post("/api/applications")
.send({ name: "My App" }) .send({ name: "My App" })
.set(defaultHeaders) .set(defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual("Application My App created successfully") expect(res.res.statusMessage).toEqual("Application My App created successfully")
expect(res.body._id).toBeDefined() expect(res.body._id).toBeDefined()
}) })
it("should apply authorization to endpoint", async () => { fit("should apply authorization to endpoint", async () => {
const otherApplication = await createApplication(request) const otherApplication = await createApplication(request)
const instance = await createInstance(request, otherApplication._id) const instance = await createInstance(request, otherApplication._id)
await builderEndpointShouldBlockNormalUsers({ await builderEndpointShouldBlockNormalUsers({
@ -49,6 +49,7 @@ describe("/applications", () => {
method: "POST", method: "POST",
url: `/api/applications`, url: `/api/applications`,
instanceId: instance._id, instanceId: instance._id,
appId: otherApplication._id,
body: { name: "My App" } body: { name: "My App" }
}) })
}) })
@ -63,7 +64,7 @@ describe("/applications", () => {
const res = await request const res = await request
.get("/api/applications") .get("/api/applications")
.set(defaultHeaders) .set(defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -77,13 +78,13 @@ describe("/applications", () => {
const blah = await request const blah = await request
.post("/api/applications") .post("/api/applications")
.send({ name: "app2", clientId: "new_client"}) .send({ name: "app2", clientId: "new_client"})
.set(defaultHeaders) .set(defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
//.expect(200) //.expect(200)
const client1Res = await request const client1Res = await request
.get(`/api/applications?clientId=${TEST_CLIENT_ID}`) .get(`/api/applications?clientId=${TEST_CLIENT_ID}`)
.set(defaultHeaders) .set(defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -92,7 +93,7 @@ describe("/applications", () => {
const client2Res = await request const client2Res = await request
.get(`/api/applications?clientId=new_client`) .get(`/api/applications?clientId=new_client`)
.set(defaultHeaders) .set(defaultHeaders())
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -109,6 +110,7 @@ describe("/applications", () => {
method: "GET", method: "GET",
url: `/api/applications`, url: `/api/applications`,
instanceId: instance._id, instanceId: instance._id,
appId: otherApplication._id,
}) })
}) })
}) })

View File

@ -4,8 +4,12 @@ const supertest = require("supertest")
const app = require("../../../app") const app = require("../../../app")
const { const {
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
ANON_LEVEL_ID,
BUILDER_LEVEL_ID,
generateAdminPermissions, generateAdminPermissions,
} = require("../../../utilities/accessLevels") } = require("../../../utilities/accessLevels")
const jwt = require("jsonwebtoken")
const env = require("../../../environment")
const TEST_CLIENT_ID = "test-client-id" const TEST_CLIENT_ID = "test-client-id"
@ -20,13 +24,24 @@ exports.supertest = async () => {
return { request, server } return { request, server }
} }
exports.defaultHeaders = { exports.defaultHeaders = (appId, instanceId) => {
Accept: "application/json", const builderUser = {
Cookie: ["builder:token=test-admin-secret"], userId: "BUILDER",
"x-user-agent": "Budibase Builder", accessLevelId: BUILDER_LEVEL_ID,
appId,
instanceId,
}
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
return {
Accept: "application/json",
Cookie: [`builder:token=${builderToken}`],
"x-user-agent": "Budibase Builder",
}
} }
exports.createModel = async (request, instanceId, model) => { exports.createModel = async (request, appId, instanceId, model) => {
model = model || { model = model || {
name: "TestModel", name: "TestModel",
type: "model", type: "model",
@ -43,19 +58,19 @@ exports.createModel = async (request, instanceId, model) => {
const res = await request const res = await request
.post(`/api/${instanceId}/models`) .post(`/api/${instanceId}/models`)
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId, instanceId))
.send(model) .send(model)
return res.body return res.body
} }
exports.createView = async (request, instanceId, view) => { exports.createView = async (request, appId, instanceId, view) => {
view = view || { view = view || {
map: "function(doc) { emit(doc[doc.key], doc._id); } ", map: "function(doc) { emit(doc[doc.key], doc._id); } ",
} }
const res = await request const res = await request
.post(`/api/${instanceId}/views`) .post(`/api/${instanceId}/views`)
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId, instanceId))
.send(view) .send(view)
return res.body return res.body
} }
@ -65,7 +80,7 @@ exports.createClientDatabase = async id => await create(id || TEST_CLIENT_ID)
exports.createApplication = async (request, name = "test_application") => { exports.createApplication = async (request, name = "test_application") => {
const res = await request const res = await request
.post("/api/applications") .post("/api/applications")
.set(exports.defaultHeaders) .set(exports.defaultHeaders())
.send({ .send({
name, name,
}) })
@ -76,8 +91,8 @@ exports.destroyClientDatabase = async () => await destroy(TEST_CLIENT_ID)
exports.createInstance = async (request, appId) => { exports.createInstance = async (request, appId) => {
const res = await request const res = await request
.post(`/api/${appId}/instances`) .post(`/api/instances`)
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId))
.send({ .send({
name: "test-instance", name: "test-instance",
}) })
@ -86,13 +101,14 @@ exports.createInstance = async (request, appId) => {
exports.createUser = async ( exports.createUser = async (
request, request,
appId,
instanceId, instanceId,
username = "babs", username = "babs",
password = "babs_password" password = "babs_password"
) => { ) => {
const res = await request const res = await request
.post(`/api/${instanceId}/users`) .post(`/api/users`)
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId, instanceId))
.send({ .send({
name: "Bill", name: "Bill",
username, username,
@ -104,6 +120,7 @@ exports.createUser = async (
const createUserWithOnePermission = async ( const createUserWithOnePermission = async (
request, request,
appId,
instanceId, instanceId,
permName, permName,
itemId itemId
@ -115,17 +132,19 @@ const createUserWithOnePermission = async (
return await createUserWithPermissions( return await createUserWithPermissions(
request, request,
appId,
instanceId, instanceId,
permissions, permissions,
"onePermOnlyUser" "onePermOnlyUser"
) )
} }
const createUserWithAdminPermissions = async (request, instanceId) => { const createUserWithAdminPermissions = async (request, appId, instanceId) => {
let permissions = await generateAdminPermissions(instanceId) let permissions = await generateAdminPermissions(instanceId)
return await createUserWithPermissions( return await createUserWithPermissions(
request, request,
appId,
instanceId, instanceId,
permissions, permissions,
"adminUser" "adminUser"
@ -134,6 +153,7 @@ const createUserWithAdminPermissions = async (request, instanceId) => {
const createUserWithAllPermissionExceptOne = async ( const createUserWithAllPermissionExceptOne = async (
request, request,
appId,
instanceId, instanceId,
permName, permName,
itemId itemId
@ -145,6 +165,7 @@ const createUserWithAllPermissionExceptOne = async (
return await createUserWithPermissions( return await createUserWithPermissions(
request, request,
appId,
instanceId, instanceId,
permissions, permissions,
"allPermsExceptOneUser" "allPermsExceptOneUser"
@ -153,6 +174,7 @@ const createUserWithAllPermissionExceptOne = async (
const createUserWithPermissions = async ( const createUserWithPermissions = async (
request, request,
appId,
instanceId, instanceId,
permissions, permissions,
username username
@ -160,12 +182,12 @@ const createUserWithPermissions = async (
const accessRes = await request const accessRes = await request
.post(`/api/${instanceId}/accesslevels`) .post(`/api/${instanceId}/accesslevels`)
.send({ name: "TestLevel", permissions }) .send({ name: "TestLevel", permissions })
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId, instanceId))
const password = `password_${username}` const password = `password_${username}`
await request await request
.post(`/api/${instanceId}/users`) .post(`/api/${instanceId}/users`)
.set(exports.defaultHeaders) .set(exports.defaultHeaders(appId, instanceId))
.send({ .send({
name: username, name: username,
username, username,
@ -173,12 +195,20 @@ const createUserWithPermissions = async (
accessLevelId: accessRes.body._id, accessLevelId: accessRes.body._id,
}) })
const db = new CouchDB(instanceId) //const db = new CouchDB(instanceId)
const designDoc = await db.get("_design/database") //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 const loginResult = await request
.post(`/api/authenticate`) .post(`/api/authenticate`)
.set({ Cookie: `budibase:appid=${designDoc.metadata.applicationId}` }) .set({ Cookie: `budibase:token=${anonToken}` })
.send({ username, password }) .send({ username, password })
// returning necessary request headers // returning necessary request headers
@ -193,12 +223,14 @@ exports.testPermissionsForEndpoint = async ({
method, method,
url, url,
body, body,
appId,
instanceId, instanceId,
permissionName, permissionName,
itemId, itemId,
}) => { }) => {
const headers = await createUserWithOnePermission( const headers = await createUserWithOnePermission(
request, request,
appId,
instanceId, instanceId,
permissionName, permissionName,
itemId itemId
@ -210,6 +242,7 @@ exports.testPermissionsForEndpoint = async ({
const noPermsHeaders = await createUserWithAllPermissionExceptOne( const noPermsHeaders = await createUserWithAllPermissionExceptOne(
request, request,
appId,
instanceId, instanceId,
permissionName, permissionName,
itemId itemId
@ -225,9 +258,14 @@ exports.builderEndpointShouldBlockNormalUsers = async ({
method, method,
url, url,
body, body,
appId,
instanceId, instanceId,
}) => { }) => {
const headers = await createUserWithAdminPermissions(request, instanceId) const headers = await createUserWithAdminPermissions(
request,
appId,
instanceId
)
await createRequest(request, method, url, body) await createRequest(request, method, url, body)
.set(headers) .set(headers)

View File

@ -24,9 +24,9 @@ describe("/instances", () => {
it("returns a success message when the instance database is successfully created", async () => { it("returns a success message when the instance database is successfully created", async () => {
const res = await request const res = await request
.post(`/api/${TEST_APP_ID}/instances`) .post(`/api/instances`)
.send({ name: "test-instance" }) .send({ name: "test-instance" })
.set(defaultHeaders) .set(defaultHeaders(TEST_APP_ID))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -42,7 +42,7 @@ describe("/instances", () => {
const instance = await createInstance(request, TEST_APP_ID); const instance = await createInstance(request, TEST_APP_ID);
const res = await request const res = await request
.delete(`/api/instances/${instance._id}`) .delete(`/api/instances/${instance._id}`)
.set(defaultHeaders) .set(defaultHeaders(TEST_APP_ID))
.expect(200) .expect(200)
expect(res.res.statusMessage).toEqual(`Instance Database ${instance._id} successfully destroyed.`); expect(res.res.statusMessage).toEqual(`Instance Database ${instance._id} successfully destroyed.`);

View File

@ -41,7 +41,7 @@ describe("/models", () => {
name: { type: "string" } name: { type: "string" }
} }
}) })
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (err, res) => { .end(async (err, res) => {
@ -73,13 +73,13 @@ describe("/models", () => {
beforeEach(async () => { beforeEach(async () => {
instance = await createInstance(request, app._id) 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 => { it("returns all the models for that instance in the response body", done => {
request request
.get(`/api/${instance._id}/models`) .get(`/api/${instance._id}/models`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .end(async (_, res) => {
@ -105,7 +105,7 @@ describe("/models", () => {
beforeEach(async () => { beforeEach(async () => {
instance = await createInstance(request, app._id) instance = await createInstance(request, app._id)
testModel = await createModel(request, instance._id, testModel) testModel = await createModel(request, app._id, instance._id, testModel)
}); });
afterEach(() => { afterEach(() => {
@ -115,7 +115,7 @@ describe("/models", () => {
it("returns a success response when a model is deleted.", async done => { it("returns a success response when a model is deleted.", async done => {
request request
.delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`) .delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .end(async (_, res) => {
@ -125,7 +125,7 @@ describe("/models", () => {
}) })
it("deletes linked references to the model after deletion", async done => { 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", name: "LinkedModel",
type: "model", type: "model",
key: "name", key: "name",
@ -148,7 +148,7 @@ describe("/models", () => {
request request
.delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`) .delete(`/api/${instance._id}/models/${testModel._id}/${testModel._rev}`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
.end(async (_, res) => { .end(async (_, res) => {

View File

@ -27,7 +27,7 @@ describe("/records", () => {
beforeEach(async () => { beforeEach(async () => {
instance = await createInstance(request, app._id) instance = await createInstance(request, app._id)
model = await createModel(request, instance._id) model = await createModel(request, app._id, instance._id)
record = { record = {
name: "Test Contact", name: "Test Contact",
status: "new", status: "new",
@ -39,13 +39,13 @@ describe("/records", () => {
const createRecord = async r => const createRecord = async r =>
await request await request
.post(`/api/${instance._id}/${model._id}/records`) .post(`/api/${model._id}/records`)
.send(r || record) .send(r || record)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
it("returns a success message when the record is created", async () => { fit("returns a success message when the record is created", async () => {
const res = await createRecord() const res = await createRecord()
expect(res.res.statusMessage).toEqual(`${model.name} created successfully`) expect(res.res.statusMessage).toEqual(`${model.name} created successfully`)
expect(res.body.name).toEqual("Test Contact") expect(res.body.name).toEqual("Test Contact")
@ -57,14 +57,14 @@ describe("/records", () => {
const existing = rec.body const existing = rec.body
const res = await request const res = await request
.post(`/api/${instance._id}/${model._id}/records`) .post(`/api/${model._id}/records`)
.send({ .send({
_id: existing._id, _id: existing._id,
_rev: existing._rev, _rev: existing._rev,
modelId: model._id, modelId: model._id,
name: "Updated Name", name: "Updated Name",
}) })
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -77,8 +77,8 @@ describe("/records", () => {
const existing = rec.body const existing = rec.body
const res = await request const res = await request
.get(`/api/${instance._id}/${model._id}/records/${existing._id}`) .get(`/api/${model._id}/records/${existing._id}`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -100,8 +100,8 @@ describe("/records", () => {
await createRecord(newRecord) await createRecord(newRecord)
const res = await request const res = await request
.get(`/api/${instance._id}/${model._id}/records`) .get(`/api/${model._id}/records`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -122,8 +122,8 @@ describe("/records", () => {
const recordIds = [record.body._id, secondRecord.body._id] const recordIds = [record.body._id, secondRecord.body._id]
const res = await request const res = await request
.post(`/api/${instance._id}/records/search`) .post(`/api/records/search`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.send({ .send({
keys: recordIds keys: recordIds
}) })
@ -137,8 +137,8 @@ describe("/records", () => {
it("load should return 404 when record does not exist", async () => { it("load should return 404 when record does not exist", async () => {
await createRecord() await createRecord()
await request await request
.get(`/api/${instance._id}/${model._id}/records/not-a-valid-id`) .get(`/api/${model._id}/records/not-a-valid-id`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(404) .expect(404)
}) })
@ -147,9 +147,9 @@ describe("/records", () => {
describe("validate", () => { describe("validate", () => {
it("should return no errors on valid record", async () => { it("should return no errors on valid record", async () => {
const result = await request const result = await request
.post(`/api/${instance._id}/${model._id}/records/validate`) .post(`/api/${model._id}/records/validate`)
.send({ name: "ivan" }) .send({ name: "ivan" })
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -159,9 +159,9 @@ describe("/records", () => {
it("should errors on invalid record", async () => { it("should errors on invalid record", async () => {
const result = await request const result = await request
.post(`/api/${instance._id}/${model._id}/records/validate`) .post(`/api/${model._id}/records/validate`)
.send({ name: 1 }) .send({ name: 1 })
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)

View File

@ -36,11 +36,11 @@ describe("/users", () => {
describe("fetch", () => { describe("fetch", () => {
it("returns a list of users from an instance db", async () => { it("returns a list of users from an instance db", async () => {
await createUser(request, instance._id, "brenda", "brendas_password") await createUser(request, app._id, instance._id, "brenda", "brendas_password")
await createUser(request, instance._id, "pam", "pam_password") await createUser(request, app._id, instance._id, "pam", "pam_password")
const res = await request const res = await request
.get(`/api/${instance._id}/users`) .get(`/api/users`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -50,7 +50,7 @@ describe("/users", () => {
}) })
it("should apply authorization to endpoint", async () => { 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({ await testPermissionsForEndpoint({
request, request,
method: "GET", method: "GET",
@ -67,7 +67,7 @@ describe("/users", () => {
it("returns a success message when a user is successfully created", async () => { it("returns a success message when a user is successfully created", async () => {
const res = await request const res = await request
.post(`/api/${instance._id}/users`) .post(`/api/${instance._id}/users`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID }) .send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID })
.expect(200) .expect(200)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)

View File

@ -40,7 +40,7 @@ describe("/views", () => {
}`, }`,
reduce: `function(keys, values) { }` reduce: `function(keys, values) { }`
}) })
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -56,14 +56,14 @@ describe("/views", () => {
describe("fetch", () => { describe("fetch", () => {
beforeEach(async () => { beforeEach(async () => {
model = await createModel(request, instance._id); model = await createModel(request, app._id, instance._id);
}); });
it("should only return custom views", async () => { it("should only return custom views", async () => {
const view = await createView() const view = await createView()
const res = await request const res = await request
.get(`/api/${instance._id}/views`) .get(`/api/${instance._id}/views`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
expect(res.body.length).toBe(1) expect(res.body.length).toBe(1)

View File

@ -64,7 +64,7 @@ describe("/workflows", () => {
it("returns a success message when the workflow is successfully created", async () => { it("returns a success message when the workflow is successfully created", async () => {
const res = await request const res = await request
.post(`/api/${instance._id}/workflows`) .post(`/api/${instance._id}/workflows`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.send(TEST_WORKFLOW) .send(TEST_WORKFLOW)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -93,7 +93,7 @@ describe("/workflows", () => {
const res = await request const res = await request
.put(`/api/${instance._id}/workflows`) .put(`/api/${instance._id}/workflows`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.send(workflow) .send(workflow)
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -108,7 +108,7 @@ describe("/workflows", () => {
await createWorkflow(); await createWorkflow();
const res = await request const res = await request
.get(`/api/${instance._id}/workflows`) .get(`/api/${instance._id}/workflows`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)
@ -130,7 +130,7 @@ describe("/workflows", () => {
await createWorkflow(); await createWorkflow();
const res = await request const res = await request
.delete(`/api/${instance._id}/workflows/${workflow.id}/${workflow.rev}`) .delete(`/api/${instance._id}/workflows/${workflow.id}/${workflow.rev}`)
.set(defaultHeaders) .set(defaultHeaders(app._id, instance._id))
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200) .expect(200)

View File

@ -6,19 +6,11 @@ const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels")
const router = Router() const router = Router()
router router
.get("/api/:instanceId/users", authorized(LIST_USERS), controller.fetch) .get("/api/users", authorized(LIST_USERS), controller.fetch)
.get( .get("/api/users/:username", authorized(USER_MANAGEMENT), controller.find)
"/api/:instanceId/users/:username", .post("/api/users", authorized(USER_MANAGEMENT), controller.create)
authorized(USER_MANAGEMENT),
controller.find
)
.post(
"/api/:instanceId/users",
authorized(USER_MANAGEMENT),
controller.create
)
.delete( .delete(
"/api/:instanceId/users/:username", "/api/users/:username",
authorized(USER_MANAGEMENT), authorized(USER_MANAGEMENT),
controller.destroy controller.destroy
) )

View File

@ -8,13 +8,13 @@ const router = Router()
router router
.get( .get(
"/api/:instanceId/views/:viewName", "/api/views/:viewName",
authorized(READ_VIEW, ctx => ctx.params.viewName), authorized(READ_VIEW, ctx => ctx.params.viewName),
recordController.fetchView recordController.fetchView
) )
.get("/api/:instanceId/views", authorized(BUILDER), viewController.fetch) .get("/api/views", authorized(BUILDER), viewController.fetch)
// .patch("/api/:databaseId/views", controller.update); // .patch("/api/:databaseId/views", controller.update);
// .delete("/api/:instanceId/views/:viewId/:revId", controller.destroy); // .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 module.exports = router

View File

@ -6,20 +6,16 @@ const { BUILDER } = require("../../utilities/accessLevels")
const router = Router() const router = 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/workflows/:id", authorized(BUILDER), controller.find)
.get( .get(
"/api/:instanceId/workflows/:id/:action", "/api/workflows/:id/:action",
authorized(BUILDER), authorized(BUILDER),
controller.fetchActionScript controller.fetchActionScript
) )
.put("/api/:instanceId/workflows", authorized(BUILDER), controller.update) .put("/api/workflows", authorized(BUILDER), controller.update)
.post("/api/:instanceId/workflows", authorized(BUILDER), controller.create) .post("/api/workflows", authorized(BUILDER), controller.create)
.post("/api/workflows/action", controller.executeAction) .post("/api/workflows/action", controller.executeAction)
.delete( .delete("/api/workflows/:id/:rev", authorized(BUILDER), controller.destroy)
"/api/:instanceId/workflows/:id/:rev",
authorized(BUILDER),
controller.destroy
)
module.exports = router module.exports = router

View File

@ -1,10 +1,11 @@
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const STATUS_CODES = require("../utilities/statusCodes") const STATUS_CODES = require("../utilities/statusCodes")
const env = require("../environment")
const accessLevelController = require("../api/controllers/accesslevel") const accessLevelController = require("../api/controllers/accesslevel")
const { const {
ADMIN_LEVEL_ID, ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
BUILDER_LEVEL_ID,
ANON_LEVEL_ID,
} = require("../utilities/accessLevels") } = require("../utilities/accessLevels")
module.exports = async (ctx, next) => { module.exports = async (ctx, next) => {
@ -21,10 +22,19 @@ module.exports = async (ctx, next) => {
const shouldAuthAsBuilder = isBuilderAgent && builderToken const shouldAuthAsBuilder = isBuilderAgent && builderToken
if (shouldAuthAsBuilder) { if (shouldAuthAsBuilder) {
const builderTokenValid = builderToken === env.ADMIN_SECRET try {
const jwtPayload = jwt.verify(builderToken, ctx.config.jwtSecret)
ctx.isAuthenticated = builderTokenValid ctx.isAuthenticated = jwtPayload.accessLevelId === BUILDER_LEVEL_ID
ctx.isBuilder = builderTokenValid ctx.user = {
...jwtPayload,
accessLevel: await getAccessLevel(
jwtPayload.instanceId,
jwtPayload.accessLevelId
),
}
} catch (_) {
// empty: do nothing
}
await next() await next()
return return
@ -46,7 +56,7 @@ module.exports = async (ctx, next) => {
jwtPayload.accessLevelId jwtPayload.accessLevelId
), ),
} }
ctx.isAuthenticated = true ctx.isAuthenticated = ctx.user.accessLevelId !== ANON_LEVEL_ID
} catch (err) { } catch (err) {
ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text) ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text)
} }
@ -57,7 +67,9 @@ module.exports = async (ctx, next) => {
const getAccessLevel = async (instanceId, accessLevelId) => { const getAccessLevel = async (instanceId, accessLevelId) => {
if ( if (
accessLevelId === POWERUSER_LEVEL_ID || accessLevelId === POWERUSER_LEVEL_ID ||
accessLevelId === ADMIN_LEVEL_ID accessLevelId === ADMIN_LEVEL_ID ||
accessLevelId === BUILDER_LEVEL_ID ||
accessLevelId === ANON_LEVEL_ID
) { ) {
return { return {
_id: accessLevelId, _id: accessLevelId,

View File

@ -2,6 +2,7 @@ const {
adminPermissions, adminPermissions,
ADMIN_LEVEL_ID, ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
BUILDER_LEVEL_ID,
BUILDER, BUILDER,
} = require("../utilities/accessLevels") } = require("../utilities/accessLevels")
@ -10,7 +11,11 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
ctx.throw(403, "Session not authenticated") 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() await next()
return return
} }
@ -20,10 +25,6 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
return return
} }
if (!ctx.user) {
ctx.throw(403, "User not found")
}
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "") const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) { if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {

View File

@ -5,6 +5,8 @@ const workflowController = require("../api/controllers/workflow")
// Access Level IDs // Access Level IDs
const ADMIN_LEVEL_ID = "ADMIN" const ADMIN_LEVEL_ID = "ADMIN"
const POWERUSER_LEVEL_ID = "POWER_USER" const POWERUSER_LEVEL_ID = "POWER_USER"
const BUILDER_LEVEL_ID = "BUILDER"
const ANON_LEVEL_ID = "ANON"
// Permissions // Permissions
const READ_MODEL = "read-model" const READ_MODEL = "read-model"
@ -28,7 +30,7 @@ const generateAdminPermissions = async instanceId => [
const generatePowerUserPermissions = async instanceId => { const generatePowerUserPermissions = async instanceId => {
const fetchModelsCtx = { const fetchModelsCtx = {
params: { user: {
instanceId, instanceId,
}, },
} }
@ -36,7 +38,7 @@ const generatePowerUserPermissions = async instanceId => {
const models = fetchModelsCtx.body const models = fetchModelsCtx.body
const fetchViewsCtx = { const fetchViewsCtx = {
params: { user: {
instanceId, instanceId,
}, },
} }
@ -44,7 +46,7 @@ const generatePowerUserPermissions = async instanceId => {
const views = fetchViewsCtx.body const views = fetchViewsCtx.body
const fetchWorkflowsCtx = { const fetchWorkflowsCtx = {
params: { user: {
instanceId, instanceId,
}, },
} }
@ -83,6 +85,8 @@ const generatePowerUserPermissions = async instanceId => {
module.exports = { module.exports = {
ADMIN_LEVEL_ID, ADMIN_LEVEL_ID,
POWERUSER_LEVEL_ID, POWERUSER_LEVEL_ID,
BUILDER_LEVEL_ID,
ANON_LEVEL_ID,
READ_MODEL, READ_MODEL,
WRITE_MODEL, WRITE_MODEL,
READ_VIEW, READ_VIEW,

View File

@ -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 })
}

View File

@ -8,7 +8,6 @@
fcRoot(FusionCharts, Charts, FusionTheme) fcRoot(FusionCharts, Charts, FusionTheme)
export let _bb export let _bb
export let _instanceId
export let model export let model
export let type = "column2d" export let type = "column2d"
@ -25,7 +24,7 @@
} }
async function fetchData() { 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) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()

View File

@ -2,7 +2,6 @@
import { onMount } from "svelte" import { onMount } from "svelte"
export let _bb export let _bb
export let _instanceId
export let model export let model
let username let username
@ -21,14 +20,14 @@
$: fields = Object.keys(schema) $: fields = Object.keys(schema)
async function fetchModel() { 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) const response = await _bb.api.get(FETCH_MODEL_URL)
modelDef = await response.json() modelDef = await response.json()
schema = modelDef.schema schema = modelDef.schema
} }
async function save() { 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 response = await _bb.api.post(SAVE_RECORD_URL, newModel)
const json = await response.json() const json = await response.json()

View File

@ -2,7 +2,6 @@
import { onMount } from "svelte" import { onMount } from "svelte"
export let _bb export let _bb
export let _instanceId
export let model export let model
export let layout = "list" export let layout = "list"
@ -12,7 +11,7 @@
async function fetchData() { async function fetchData() {
if (!model || !model.length) return 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) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()

View File

@ -3,14 +3,13 @@
export let _bb export let _bb
export let onLoad export let onLoad
export let _instanceId
export let model export let model
let headers = [] let headers = []
let store = _bb.store let store = _bb.store
async function fetchData() { 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) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {

View File

@ -2,7 +2,6 @@
import { onMount } from "svelte" import { onMount } from "svelte"
export let _bb export let _bb
export let _instanceId
export let model export let model
export let layout = "list" export let layout = "list"
@ -13,7 +12,7 @@
async function fetchData() { async function fetchData() {
if (!model || !model.length) return 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) const response = await _bb.api.get(FETCH_RECORDS_URL)
if (response.status === 200) { if (response.status === 200) {
const json = await response.json() const json = await response.json()