Merge branch 'feature/backend-routing' of github.com:Budibase/budibase into component-sdk
This commit is contained in:
commit
aaa6aa4c72
|
@ -81,7 +81,8 @@
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte-loading-spinners": "^0.1.1",
|
"svelte-loading-spinners": "^0.1.1",
|
||||||
"svelte-portal": "^0.1.0",
|
"svelte-portal": "^0.1.0",
|
||||||
"yup": "^0.29.2"
|
"yup": "^0.29.2",
|
||||||
|
"uuid": "^8.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.5.5",
|
"@babel/core": "^7.5.5",
|
||||||
|
|
|
@ -50,6 +50,7 @@ export const getFrontendStore = () => {
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
const screens = await api.get("/api/screens").then(r => r.json())
|
const screens = await api.get("/api/screens").then(r => r.json())
|
||||||
|
const routing = await api.get("/api/routing").then(r => r.json())
|
||||||
|
|
||||||
const mainScreens = screens.filter(screen =>
|
const mainScreens = screens.filter(screen =>
|
||||||
screen._id.includes(pkg.pages.main._id)
|
screen._id.includes(pkg.pages.main._id)
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: `Create from scratch`,
|
name: `Create from scratch`,
|
||||||
create: () => createScreen(),
|
create: () => createScreen(),
|
||||||
}
|
}
|
||||||
|
|
||||||
const createScreen = () => ({
|
const createScreen = () => {
|
||||||
props: {
|
return new Screen()
|
||||||
_id: "",
|
.mainType("div")
|
||||||
_component: "@budibase/standard-components/container",
|
.component("@budibase/standard-components/container")
|
||||||
_styles: {
|
.json()
|
||||||
normal: {},
|
}
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
type: "div",
|
|
||||||
_children: [],
|
|
||||||
_instanceName: "",
|
|
||||||
},
|
|
||||||
route: "",
|
|
||||||
name: "screen-id",
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: `New Row (Empty)`,
|
name: `New Row (Empty)`,
|
||||||
create: () => createScreen(),
|
create: () => createScreen(),
|
||||||
}
|
}
|
||||||
|
|
||||||
const createScreen = () => ({
|
const createScreen = () => {
|
||||||
props: {
|
return new Screen()
|
||||||
_id: "",
|
.component("@budibase/standard-components/newrow")
|
||||||
_component: "@budibase/standard-components/newrow",
|
.table("")
|
||||||
_styles: {
|
.json()
|
||||||
normal: {},
|
}
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_children: [],
|
|
||||||
_instanceName: "",
|
|
||||||
table: "",
|
|
||||||
},
|
|
||||||
route: "",
|
|
||||||
name: "screen-id",
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: `Row Detail (Empty)`,
|
name: `Row Detail (Empty)`,
|
||||||
create: () => createScreen(),
|
create: () => createScreen(),
|
||||||
}
|
}
|
||||||
|
|
||||||
const createScreen = () => ({
|
const createScreen = () => {
|
||||||
props: {
|
return new Screen()
|
||||||
_id: "",
|
.component("@budibase/standard-components/rowdetail")
|
||||||
_component: "@budibase/standard-components/rowdetail",
|
.table("")
|
||||||
_styles: {
|
.json()
|
||||||
normal: {},
|
}
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_children: [],
|
|
||||||
_instanceName: "",
|
|
||||||
table: "",
|
|
||||||
},
|
|
||||||
route: "",
|
|
||||||
name: "screen-id",
|
|
||||||
})
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ const createTemplateOverride = (frontendState, create) => () => {
|
||||||
}
|
}
|
||||||
screen.props._id = uuid()
|
screen.props._id = uuid()
|
||||||
screen.name = screen.props._id
|
screen.name = screen.props._id
|
||||||
screen.route = screen.route.toLowerCase()
|
screen.routing.route = screen.routing.route.toLowerCase()
|
||||||
return screen
|
return screen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
import sanitizeUrl from "./sanitizeUrl"
|
import sanitizeUrl from "./utils/sanitizeUrl"
|
||||||
import { rowListUrl } from "./rowListScreen"
|
import { Component } from "./utils/Component"
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
import {
|
||||||
|
makeBreadcrumbContainer,
|
||||||
|
makeMainContainer,
|
||||||
|
makeTitleContainer,
|
||||||
|
makeSaveButton,
|
||||||
|
} from "./utils/commonComponents"
|
||||||
|
|
||||||
export default function(tables) {
|
export default function(tables) {
|
||||||
return tables.map(table => {
|
return tables.map(table => {
|
||||||
|
@ -14,242 +21,26 @@ export default function(tables) {
|
||||||
export const newRowUrl = table => sanitizeUrl(`/${table.name}/new`)
|
export const newRowUrl = table => sanitizeUrl(`/${table.name}/new`)
|
||||||
export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE"
|
export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE"
|
||||||
|
|
||||||
const createScreen = table => ({
|
function generateTitleContainer(table) {
|
||||||
props: {
|
return makeTitleContainer("New Row").addChild(makeSaveButton(table))
|
||||||
_id: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
|
}
|
||||||
_component: "@budibase/standard-components/newrow",
|
|
||||||
_styles: {
|
const createScreen = table => {
|
||||||
normal: {},
|
const dataform = new Component(
|
||||||
hover: {},
|
"@budibase/standard-components/dataformwide"
|
||||||
active: {},
|
).instanceName("Form")
|
||||||
selected: {},
|
|
||||||
},
|
const container = makeMainContainer()
|
||||||
table: table._id,
|
.addChild(makeBreadcrumbContainer(table.name, "New"))
|
||||||
_children: [
|
.addChild(generateTitleContainer(table))
|
||||||
{
|
.addChild(dataform)
|
||||||
_id: "ccad6cc135c7947a7ba9c631f655d6e0f",
|
|
||||||
_component: "@budibase/standard-components/container",
|
return new Screen()
|
||||||
_styles: {
|
.component("@budibase/standard-components/newrow")
|
||||||
normal: {
|
.table(table._id)
|
||||||
width: "700px",
|
.route(newRowUrl(table))
|
||||||
padding: "0px",
|
.instanceName(`${table.name} - New`)
|
||||||
background: "white",
|
.name("")
|
||||||
"border-radius": "0.5rem",
|
.addChild(container)
|
||||||
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
.json()
|
||||||
margin: "auto",
|
}
|
||||||
"margin-top": "20px",
|
|
||||||
"padding-top": "48px",
|
|
||||||
"padding-bottom": "48px",
|
|
||||||
"padding-right": "48px",
|
|
||||||
"padding-left": "48px",
|
|
||||||
"margin-bottom": "20px",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
onLoad: [],
|
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Container",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "c6e91622ba7984f468f70bf4bf5120246",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"font-size": "14px",
|
|
||||||
color: "#757575",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
onLoad: [],
|
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Breadcrumbs",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "caa33353c252c4931b2a51b48a559a7fc",
|
|
||||||
_component: "@budibase/standard-components/link",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
color: "#757575",
|
|
||||||
"text-transform": "capitalize",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
color: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
url: `/${table.name.toLowerCase()}`,
|
|
||||||
openInNewTab: false,
|
|
||||||
text: table.name,
|
|
||||||
color: "",
|
|
||||||
hoverColor: "",
|
|
||||||
underline: false,
|
|
||||||
fontSize: "",
|
|
||||||
fontFamily: "initial",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Back Link",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c6e218170201040e7a74e2c8304fe1860",
|
|
||||||
_component: "@budibase/standard-components/text",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"margin-right": "4px",
|
|
||||||
"margin-left": "4px",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: ">",
|
|
||||||
type: "none",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Arrow",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c799da1fa3a84442e947cc9199518f64c",
|
|
||||||
_component: "@budibase/standard-components/text",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
color: "#000000",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: "New",
|
|
||||||
type: "none",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Identifier",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "cbd1637cd1e274287a3c28ef0bf235d08",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "row",
|
|
||||||
"justify-content": "space-between",
|
|
||||||
"align-items": "center",
|
|
||||||
"margin-top": "32px",
|
|
||||||
"margin-bottom": "32px",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
onLoad: [],
|
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Title Container",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "c98d3675d04114558bbf28661c5ccfb8e",
|
|
||||||
_component: "@budibase/standard-components/heading",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
margin: "0px",
|
|
||||||
"margin-bottom": "0px",
|
|
||||||
"margin-right": "0px",
|
|
||||||
"margin-top": "0px",
|
|
||||||
"margin-left": "0px",
|
|
||||||
flex: "1 1 auto",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
text: "New Row",
|
|
||||||
type: "h3",
|
|
||||||
_instanceName: "Title",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "cae402bd3c6a44618a8341bf7ab9ab086",
|
|
||||||
_component: "@budibase/standard-components/button",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
background: "#000000",
|
|
||||||
"border-width": "0",
|
|
||||||
"border-style": "None",
|
|
||||||
color: "#fff",
|
|
||||||
"font-family": "Inter",
|
|
||||||
"font-weight": "500",
|
|
||||||
"font-size": "14px",
|
|
||||||
"margin-left": "16px",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: "Save",
|
|
||||||
className: "",
|
|
||||||
disabled: false,
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
contextPath: "data",
|
|
||||||
tableId: table._id,
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Save Row",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
url: rowListUrl(table),
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Navigate To",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: "Save Button",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c5e6c98d7363640f9ad3a7d19c8c10f67",
|
|
||||||
_component: "@budibase/standard-components/dataformwide",
|
|
||||||
_styles: {
|
|
||||||
normal: {},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Form",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: `${table.name} - New`,
|
|
||||||
_code: "",
|
|
||||||
},
|
|
||||||
route: newRowUrl(table),
|
|
||||||
name: "",
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,5 +1,13 @@
|
||||||
import sanitizeUrl from "./sanitizeUrl"
|
import sanitizeUrl from "./utils/sanitizeUrl"
|
||||||
import { rowListUrl } from "./rowListScreen"
|
import { rowListUrl } from "./rowListScreen"
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
import { Component } from "./utils/Component"
|
||||||
|
import {
|
||||||
|
makeMainContainer,
|
||||||
|
makeBreadcrumbContainer,
|
||||||
|
makeTitleContainer,
|
||||||
|
makeSaveButton,
|
||||||
|
} from "./utils/commonComponents"
|
||||||
|
|
||||||
export default function(tables) {
|
export default function(tables) {
|
||||||
return tables.map(table => {
|
return tables.map(table => {
|
||||||
|
@ -17,288 +25,78 @@ export default function(tables) {
|
||||||
export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE"
|
export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE"
|
||||||
export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`)
|
export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`)
|
||||||
|
|
||||||
const createScreen = (table, heading) => ({
|
function generateTitleContainer(table, title) {
|
||||||
props: {
|
// have to override style for this, its missing margin
|
||||||
_id: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
|
const saveButton = makeSaveButton(table).normalStyle({
|
||||||
_component: "@budibase/standard-components/rowdetail",
|
background: "#000000",
|
||||||
_styles: {
|
"border-width": "0",
|
||||||
normal: {},
|
"border-style": "None",
|
||||||
hover: {},
|
color: "#fff",
|
||||||
active: {},
|
"font-family": "Inter",
|
||||||
selected: {},
|
"font-weight": "500",
|
||||||
},
|
"font-size": "14px",
|
||||||
table: table._id,
|
})
|
||||||
_children: [
|
|
||||||
{
|
const deleteButton = new Component("@budibase/standard-components/button")
|
||||||
_id: "ccad6cc135c7947a7ba9c631f655d6e0f",
|
.normalStyle({
|
||||||
_component: "@budibase/standard-components/container",
|
background: "transparent",
|
||||||
_styles: {
|
"border-width": "0",
|
||||||
normal: {
|
"border-style": "None",
|
||||||
width: "700px",
|
color: "#9e9e9e",
|
||||||
padding: "0px",
|
"font-family": "Inter",
|
||||||
background: "white",
|
"font-weight": "500",
|
||||||
"border-radius": "0.5rem",
|
"font-size": "14px",
|
||||||
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
"margin-right": "8px",
|
||||||
margin: "auto",
|
"margin-left": "16px",
|
||||||
"margin-top": "20px",
|
})
|
||||||
"padding-top": "48px",
|
.hoverStyle({
|
||||||
"padding-bottom": "48px",
|
background: "transparent",
|
||||||
"padding-right": "48px",
|
color: "#4285f4",
|
||||||
"padding-left": "48px",
|
})
|
||||||
"margin-bottom": "20px",
|
.text("Delete")
|
||||||
|
.customProps({
|
||||||
|
className: "",
|
||||||
|
disabled: false,
|
||||||
|
onClick: [
|
||||||
|
{
|
||||||
|
parameters: {
|
||||||
|
rowId: "{{ data._id }}",
|
||||||
|
revId: "{{ data._rev }}",
|
||||||
|
tableId: table._id,
|
||||||
},
|
},
|
||||||
hover: {},
|
"##eventHandlerType": "Delete Row",
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
},
|
||||||
_code: "",
|
{
|
||||||
className: "",
|
parameters: {
|
||||||
onLoad: [],
|
url: rowListUrl(table),
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Container",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "c6e91622ba7984f468f70bf4bf5120246",
|
|
||||||
_component: "@budibase/standard-components/container",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"font-size": "14px",
|
|
||||||
color: "#757575",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
onLoad: [],
|
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Breadcrumbs",
|
|
||||||
_children: [
|
|
||||||
{
|
|
||||||
_id: "caa33353c252c4931b2a51b48a559a7fc",
|
|
||||||
_component: "@budibase/standard-components/link",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
color: "#757575",
|
|
||||||
"text-transform": "capitalize",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
color: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
url: `/${table.name.toLowerCase()}`,
|
|
||||||
openInNewTab: false,
|
|
||||||
text: table.name,
|
|
||||||
color: "",
|
|
||||||
hoverColor: "",
|
|
||||||
underline: false,
|
|
||||||
fontSize: "",
|
|
||||||
fontFamily: "initial",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Back Link",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c6e218170201040e7a74e2c8304fe1860",
|
|
||||||
_component: "@budibase/standard-components/text",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
"margin-right": "4px",
|
|
||||||
"margin-left": "4px",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: ">",
|
|
||||||
type: "none",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Arrow",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c799da1fa3a84442e947cc9199518f64c",
|
|
||||||
_component: "@budibase/standard-components/text",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
color: "#000000",
|
|
||||||
"text-transform": "capitalize",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: heading || "Edit",
|
|
||||||
type: "none",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Identifier",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
"##eventHandlerType": "Navigate To",
|
||||||
_id: "cbd1637cd1e274287a3c28ef0bf235d08",
|
},
|
||||||
_component: "@budibase/standard-components/container",
|
],
|
||||||
_styles: {
|
})
|
||||||
normal: {
|
.instanceName("Delete Button")
|
||||||
display: "flex",
|
|
||||||
"flex-direction": "row",
|
return makeTitleContainer(title)
|
||||||
"justify-content": "space-between",
|
.addChild(deleteButton)
|
||||||
"align-items": "center",
|
.addChild(saveButton)
|
||||||
"margin-top": "32px",
|
}
|
||||||
"margin-bottom": "32px",
|
|
||||||
},
|
const createScreen = (table, heading) => {
|
||||||
hover: {},
|
const dataform = new Component(
|
||||||
active: {},
|
"@budibase/standard-components/dataformwide"
|
||||||
selected: {},
|
).instanceName("Form")
|
||||||
},
|
|
||||||
_code: "",
|
const container = makeMainContainer()
|
||||||
className: "",
|
.addChild(makeBreadcrumbContainer(table.name, heading || "Edit"))
|
||||||
onLoad: [],
|
.addChild(generateTitleContainer(table, heading || "Edit Row"))
|
||||||
type: "div",
|
.addChild(dataform)
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Title Container",
|
return new Screen()
|
||||||
_children: [
|
.component("@budibase/standard-components/rowdetail")
|
||||||
{
|
.table(table._id)
|
||||||
_id: "c98d3675d04114558bbf28661c5ccfb8e",
|
.instanceName(`${table.name} - Detail`)
|
||||||
_component: "@budibase/standard-components/heading",
|
.route(rowDetailUrl(table))
|
||||||
_styles: {
|
.name("")
|
||||||
normal: {
|
.addChild(container)
|
||||||
margin: "0px",
|
.json()
|
||||||
"margin-bottom": "0px",
|
}
|
||||||
"margin-right": "0px",
|
|
||||||
"margin-top": "0px",
|
|
||||||
"margin-left": "0px",
|
|
||||||
flex: "1 1 auto",
|
|
||||||
"text-transform": "capitalize",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
text: heading || "Edit Row",
|
|
||||||
type: "h3",
|
|
||||||
_instanceName: "Title",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c0a162cfb7d1c4bcfa8d24c290ccd1fd6",
|
|
||||||
_component: "@budibase/standard-components/button",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
background: "transparent",
|
|
||||||
"border-width": "0",
|
|
||||||
"border-style": "None",
|
|
||||||
color: "#9e9e9e",
|
|
||||||
"font-family": "Inter",
|
|
||||||
"font-weight": "500",
|
|
||||||
"font-size": "14px",
|
|
||||||
"margin-right": "8px",
|
|
||||||
"margin-left": "16px",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: "transparent",
|
|
||||||
color: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: "Delete",
|
|
||||||
className: "",
|
|
||||||
disabled: false,
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
rowId: "{{ data._id }}",
|
|
||||||
revId: "{{ data._rev }}",
|
|
||||||
tableId: table._id,
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Delete Row",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
url: rowListUrl(table),
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Navigate To",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: "Delete Button",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "cae402bd3c6a44618a8341bf7ab9ab086",
|
|
||||||
_component: "@budibase/standard-components/button",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
background: "#000000",
|
|
||||||
"border-width": "0",
|
|
||||||
"border-style": "None",
|
|
||||||
color: "#fff",
|
|
||||||
"font-family": "Inter",
|
|
||||||
"font-weight": "500",
|
|
||||||
"font-size": "14px",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: "Save",
|
|
||||||
className: "",
|
|
||||||
disabled: false,
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
contextPath: "data",
|
|
||||||
tableId: table._id,
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Save Row",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
url: rowListUrl(table),
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Navigate To",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: "Save Button",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c5e6c98d7363640f9ad3a7d19c8c10f67",
|
|
||||||
_component: "@budibase/standard-components/dataformwide",
|
|
||||||
_styles: {
|
|
||||||
normal: {},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Form",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: `${table.name} - Detail`,
|
|
||||||
_code: "",
|
|
||||||
},
|
|
||||||
route: rowDetailUrl(table),
|
|
||||||
name: "",
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import sanitizeUrl from "./sanitizeUrl"
|
import sanitizeUrl from "./utils/sanitizeUrl"
|
||||||
import { newRowUrl } from "./newRowScreen"
|
import { newRowUrl } from "./newRowScreen"
|
||||||
|
import { Screen } from "./utils/Screen"
|
||||||
|
import { Component } from "./utils/Component"
|
||||||
|
|
||||||
export default function(tables) {
|
export default function(tables) {
|
||||||
return tables.map(table => {
|
return tables.map(table => {
|
||||||
|
@ -14,159 +16,103 @@ export default function(tables) {
|
||||||
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
|
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
|
||||||
export const rowListUrl = table => sanitizeUrl(`/${table.name}`)
|
export const rowListUrl = table => sanitizeUrl(`/${table.name}`)
|
||||||
|
|
||||||
const createScreen = table => ({
|
function generateTitleContainer(table) {
|
||||||
props: {
|
const newButton = new Component("@budibase/standard-components/button")
|
||||||
_id: "c7365379815e4457dbe703a886c2da43b",
|
.normalStyle({
|
||||||
_component: "@budibase/standard-components/container",
|
background: "#000000",
|
||||||
_styles: {
|
"border-width": "0",
|
||||||
normal: {},
|
"border-style": "None",
|
||||||
hover: {},
|
color: "#fff",
|
||||||
active: {},
|
"font-family": "Inter",
|
||||||
selected: {},
|
"font-weight": "500",
|
||||||
},
|
"font-size": "14px",
|
||||||
type: "div",
|
})
|
||||||
_children: [
|
.hoverStyle({
|
||||||
{
|
background: "#4285f4",
|
||||||
_id: "cf51241fc063d4d87be032dd509fe0244",
|
})
|
||||||
_component: "@budibase/standard-components/container",
|
.text("Create New")
|
||||||
_styles: {
|
.customProps({
|
||||||
normal: {
|
className: "",
|
||||||
background: "white",
|
disabled: false,
|
||||||
"border-radius": "0.5rem",
|
onClick: [
|
||||||
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
{
|
||||||
margin: "auto",
|
parameters: {
|
||||||
"margin-top": "20px",
|
url: newRowUrl(table),
|
||||||
"border-width": "2px",
|
|
||||||
"border-color": "rgba(0, 0, 0, 0.1)",
|
|
||||||
"border-style": "None",
|
|
||||||
"padding-top": "48px",
|
|
||||||
"padding-bottom": "48px",
|
|
||||||
"padding-right": "48px",
|
|
||||||
"padding-left": "48px",
|
|
||||||
"margin-bottom": "20px",
|
|
||||||
},
|
},
|
||||||
hover: {},
|
"##eventHandlerType": "Navigate To",
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
},
|
||||||
_code: "",
|
],
|
||||||
className: "",
|
})
|
||||||
onLoad: [],
|
.instanceName("New Button")
|
||||||
type: "div",
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
const heading = new Component("@budibase/standard-components/heading")
|
||||||
_instanceName: "Container",
|
.normalStyle({
|
||||||
_children: [
|
margin: "0px",
|
||||||
{
|
flex: "1 1 auto",
|
||||||
_id: "c73294c301fd145aabe9bbbbd96a150ac",
|
"text-transform": "capitalize",
|
||||||
_component: "@budibase/standard-components/container",
|
})
|
||||||
_styles: {
|
.type("h3")
|
||||||
normal: {
|
.instanceName("Title")
|
||||||
display: "flex",
|
.text(table.name)
|
||||||
"flex-direction": "row",
|
|
||||||
"justify-content": "space-between",
|
return new Component("@budibase/standard-components/container")
|
||||||
"align-items": "center",
|
.type("div")
|
||||||
"margin-bottom": "32px",
|
.normalStyle({
|
||||||
},
|
display: "flex",
|
||||||
hover: {},
|
"flex-direction": "row",
|
||||||
active: {},
|
"justify-content": "space-between",
|
||||||
selected: {},
|
"align-items": "center",
|
||||||
},
|
"margin-bottom": "32px",
|
||||||
_code: "",
|
})
|
||||||
className: "",
|
.instanceName("Title Container")
|
||||||
onLoad: [],
|
.addChild(heading)
|
||||||
type: "div",
|
.addChild(newButton)
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
}
|
||||||
_instanceName: "Title Container",
|
|
||||||
_children: [
|
const createScreen = table => {
|
||||||
{
|
const datagrid = new Component("@budibase/standard-components/datagrid")
|
||||||
_id: "c2b77901df95a4d1ca7204c58300bc94b",
|
.customProps({
|
||||||
_component: "@budibase/standard-components/heading",
|
datasource: {
|
||||||
_styles: {
|
label: table.name,
|
||||||
normal: {
|
name: `all_${table._id}`,
|
||||||
margin: "0px",
|
tableId: table._id,
|
||||||
flex: "1 1 auto",
|
type: "table",
|
||||||
"text-transform": "capitalize",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
className: "",
|
|
||||||
text: table.name,
|
|
||||||
type: "h3",
|
|
||||||
_instanceName: "Title",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "c12a82d77baf24ca9922ea0af7cd4f723",
|
|
||||||
_component: "@budibase/standard-components/button",
|
|
||||||
_styles: {
|
|
||||||
normal: {
|
|
||||||
background: "#000000",
|
|
||||||
"border-width": "0",
|
|
||||||
"border-style": "None",
|
|
||||||
color: "#fff",
|
|
||||||
"font-family": "Inter",
|
|
||||||
"font-weight": "500",
|
|
||||||
"font-size": "14px",
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
background: "#4285f4",
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
text: "Create New",
|
|
||||||
className: "",
|
|
||||||
disabled: false,
|
|
||||||
onClick: [
|
|
||||||
{
|
|
||||||
parameters: {
|
|
||||||
url: newRowUrl(table),
|
|
||||||
},
|
|
||||||
"##eventHandlerType": "Navigate To",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
_instanceName: "New Button",
|
|
||||||
_children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: "ca686a2ed89c943e6bafb63fa66a3ead3",
|
|
||||||
_component: "@budibase/standard-components/datagrid",
|
|
||||||
_styles: {
|
|
||||||
normal: {},
|
|
||||||
hover: {},
|
|
||||||
active: {},
|
|
||||||
selected: {},
|
|
||||||
},
|
|
||||||
_code: "",
|
|
||||||
datasource: {
|
|
||||||
label: table.name,
|
|
||||||
name: `all_${table._id}`,
|
|
||||||
tableId: table._id,
|
|
||||||
type: "table",
|
|
||||||
},
|
|
||||||
editable: false,
|
|
||||||
theme: "alpine",
|
|
||||||
height: "540",
|
|
||||||
pagination: true,
|
|
||||||
_instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
|
|
||||||
_instanceName: "Grid",
|
|
||||||
_children: [],
|
|
||||||
detailUrl: `${table.name.toLowerCase()}/:id`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
editable: false,
|
||||||
_instanceName: `${table.name} - List`,
|
theme: "alpine",
|
||||||
_code: "",
|
height: "540",
|
||||||
className: "",
|
pagination: true,
|
||||||
onLoad: [],
|
detailUrl: `${table.name.toLowerCase()}/:id`,
|
||||||
},
|
})
|
||||||
route: rowListUrl(table),
|
.instanceName("Grid")
|
||||||
name: "",
|
|
||||||
})
|
const mainContainer = new Component("@budibase/standard-components/container")
|
||||||
|
.normalStyle({
|
||||||
|
background: "white",
|
||||||
|
"border-radius": "0.5rem",
|
||||||
|
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
||||||
|
margin: "auto",
|
||||||
|
"margin-top": "20px",
|
||||||
|
"border-width": "2px",
|
||||||
|
"border-color": "rgba(0, 0, 0, 0.1)",
|
||||||
|
"border-style": "None",
|
||||||
|
"padding-top": "48px",
|
||||||
|
"padding-bottom": "48px",
|
||||||
|
"padding-right": "48px",
|
||||||
|
"padding-left": "48px",
|
||||||
|
"margin-bottom": "20px",
|
||||||
|
})
|
||||||
|
.type("div")
|
||||||
|
.instanceName("Container")
|
||||||
|
.addChild(generateTitleContainer(table))
|
||||||
|
.addChild(datagrid)
|
||||||
|
|
||||||
|
return new Screen()
|
||||||
|
.component("@budibase/standard-components/container")
|
||||||
|
.mainType("div")
|
||||||
|
.route(rowListUrl(table))
|
||||||
|
.instanceName(`${table.name} - List`)
|
||||||
|
.name("")
|
||||||
|
.addChild(mainContainer)
|
||||||
|
.json()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
|
||||||
|
export class BaseStructure {
|
||||||
|
constructor(isScreen) {
|
||||||
|
this._isScreen = isScreen
|
||||||
|
this._children = []
|
||||||
|
this._json = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(child) {
|
||||||
|
this._children.push(child)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
customProps(props) {
|
||||||
|
for (let key of Object.keys(props)) {
|
||||||
|
this._json[key] = props[key]
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
json() {
|
||||||
|
const structure = cloneDeep(this._json)
|
||||||
|
if (this._children.length !== 0) {
|
||||||
|
for (let child of this._children) {
|
||||||
|
if (this._isScreen) {
|
||||||
|
structure.props._children.push(child.json())
|
||||||
|
} else {
|
||||||
|
structure._children.push(child.json())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return structure
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { cloneDeep } from "lodash/fp"
|
||||||
|
import { v4 } from "uuid"
|
||||||
|
import { BaseStructure } from "./BaseStructure"
|
||||||
|
|
||||||
|
export class Component extends BaseStructure {
|
||||||
|
constructor(name) {
|
||||||
|
super(false)
|
||||||
|
this._children = []
|
||||||
|
this._json = {
|
||||||
|
_id: v4(),
|
||||||
|
_component: name,
|
||||||
|
_styles: {
|
||||||
|
normal: {},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_code: "",
|
||||||
|
className: "",
|
||||||
|
onLoad: [],
|
||||||
|
type: "",
|
||||||
|
_instanceName: "",
|
||||||
|
_children: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type(type) {
|
||||||
|
this._json.type = type
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
normalStyle(styling) {
|
||||||
|
this._json._styles.normal = styling
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
hoverStyle(styling) {
|
||||||
|
this._json._styles.hover = styling
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
text(text) {
|
||||||
|
this._json.text = text
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: do we need this
|
||||||
|
instanceName(name) {
|
||||||
|
this._json._instanceName = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { BaseStructure } from "./BaseStructure"
|
||||||
|
|
||||||
|
export class Screen extends BaseStructure {
|
||||||
|
constructor() {
|
||||||
|
super(true)
|
||||||
|
this._json = {
|
||||||
|
props: {
|
||||||
|
_id: "",
|
||||||
|
_component: "",
|
||||||
|
_styles: {
|
||||||
|
normal: {},
|
||||||
|
hover: {},
|
||||||
|
active: {},
|
||||||
|
selected: {},
|
||||||
|
},
|
||||||
|
_children: [],
|
||||||
|
_instanceName: "",
|
||||||
|
},
|
||||||
|
routing: {
|
||||||
|
route: "",
|
||||||
|
accessLevelId: "",
|
||||||
|
},
|
||||||
|
name: "screen-id",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component(name) {
|
||||||
|
this._json.props._component = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
table(tableName) {
|
||||||
|
this._json.props.table = tableName
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
mainType(type) {
|
||||||
|
this._json.type = type
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
route(route) {
|
||||||
|
this._json.routing.route = route
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
name(name) {
|
||||||
|
this._json.name = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceName(name) {
|
||||||
|
this._json.props._instanceName = name
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
import { Component } from "./Component"
|
||||||
|
import { rowListUrl } from "../rowListScreen"
|
||||||
|
|
||||||
|
export function makeLinkComponent(tableName) {
|
||||||
|
return new Component("@budibase/standard-components/link")
|
||||||
|
.normalStyle({
|
||||||
|
color: "#757575",
|
||||||
|
"text-transform": "capitalize",
|
||||||
|
})
|
||||||
|
.hoverStyle({
|
||||||
|
color: "#4285f4",
|
||||||
|
})
|
||||||
|
.text(tableName)
|
||||||
|
.customProps({
|
||||||
|
url: `/${tableName.toLowerCase()}`,
|
||||||
|
openInNewTab: false,
|
||||||
|
color: "",
|
||||||
|
hoverColor: "",
|
||||||
|
underline: false,
|
||||||
|
fontSize: "",
|
||||||
|
fontFamily: "initial",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeMainContainer() {
|
||||||
|
return new Component("@budibase/standard-components/container")
|
||||||
|
.type("div")
|
||||||
|
.normalStyle({
|
||||||
|
width: "700px",
|
||||||
|
padding: "0px",
|
||||||
|
background: "white",
|
||||||
|
"border-radius": "0.5rem",
|
||||||
|
"box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
|
||||||
|
margin: "auto",
|
||||||
|
"margin-top": "20px",
|
||||||
|
"padding-top": "48px",
|
||||||
|
"padding-bottom": "48px",
|
||||||
|
"padding-right": "48px",
|
||||||
|
"padding-left": "48px",
|
||||||
|
"margin-bottom": "20px",
|
||||||
|
})
|
||||||
|
.instanceName("Container")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeBreadcrumbContainer(tableName, text, capitalise = false) {
|
||||||
|
const link = makeLinkComponent(tableName).instanceName("Back Link")
|
||||||
|
|
||||||
|
const arrowText = new Component("@budibase/standard-components/text")
|
||||||
|
.type("none")
|
||||||
|
.normalStyle({
|
||||||
|
"margin-right": "4px",
|
||||||
|
"margin-left": "4px",
|
||||||
|
})
|
||||||
|
.text(">")
|
||||||
|
.instanceName("Arrow")
|
||||||
|
|
||||||
|
const textStyling = {
|
||||||
|
color: "#000000",
|
||||||
|
}
|
||||||
|
if (capitalise) {
|
||||||
|
textStyling["text-transform"] = "capitalize"
|
||||||
|
}
|
||||||
|
const identifierText = new Component("@budibase/standard-components/text")
|
||||||
|
.type("none")
|
||||||
|
.normalStyle(textStyling)
|
||||||
|
.text(text)
|
||||||
|
.instanceName("Identifier")
|
||||||
|
|
||||||
|
return new Component("@budibase/standard-components/container")
|
||||||
|
.type("div")
|
||||||
|
.normalStyle({
|
||||||
|
"font-size": "14px",
|
||||||
|
color: "#757575",
|
||||||
|
})
|
||||||
|
.instanceName("Breadcrumbs")
|
||||||
|
.addChild(link)
|
||||||
|
.addChild(arrowText)
|
||||||
|
.addChild(identifierText)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeSaveButton(table) {
|
||||||
|
return new Component("@budibase/standard-components/button")
|
||||||
|
.normalStyle({
|
||||||
|
background: "#000000",
|
||||||
|
"border-width": "0",
|
||||||
|
"border-style": "None",
|
||||||
|
color: "#fff",
|
||||||
|
"font-family": "Inter",
|
||||||
|
"font-weight": "500",
|
||||||
|
"font-size": "14px",
|
||||||
|
"margin-left": "16px",
|
||||||
|
})
|
||||||
|
.hoverStyle({
|
||||||
|
background: "#4285f4",
|
||||||
|
})
|
||||||
|
.text("Save")
|
||||||
|
.customProps({
|
||||||
|
className: "",
|
||||||
|
disabled: false,
|
||||||
|
onClick: [
|
||||||
|
{
|
||||||
|
parameters: {
|
||||||
|
contextPath: "data",
|
||||||
|
tableId: table._id,
|
||||||
|
},
|
||||||
|
"##eventHandlerType": "Save Row",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parameters: {
|
||||||
|
url: rowListUrl(table),
|
||||||
|
},
|
||||||
|
"##eventHandlerType": "Navigate To",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.instanceName("Save Button")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function makeTitleContainer(title) {
|
||||||
|
const heading = new Component("@budibase/standard-components/heading")
|
||||||
|
.normalStyle({
|
||||||
|
margin: "0px",
|
||||||
|
"margin-bottom": "0px",
|
||||||
|
"margin-right": "0px",
|
||||||
|
"margin-top": "0px",
|
||||||
|
"margin-left": "0px",
|
||||||
|
flex: "1 1 auto",
|
||||||
|
})
|
||||||
|
.type("h3")
|
||||||
|
.instanceName("Title")
|
||||||
|
.text(title)
|
||||||
|
|
||||||
|
return new Component("@budibase/standard-components/container")
|
||||||
|
.type("div")
|
||||||
|
.normalStyle({
|
||||||
|
display: "flex",
|
||||||
|
"flex-direction": "row",
|
||||||
|
"justify-content": "space-between",
|
||||||
|
"align-items": "center",
|
||||||
|
"margin-top": "32px",
|
||||||
|
"margin-bottom": "32px",
|
||||||
|
})
|
||||||
|
.instanceName("Title Container")
|
||||||
|
.addChild(heading)
|
||||||
|
}
|
|
@ -59,10 +59,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create autolink to newly created list page
|
// Create autolink to newly created list page
|
||||||
const listPage = screens.find(screen =>
|
const listScreen = screens.find(screen =>
|
||||||
screen.props._instanceName.endsWith("List")
|
screen.props._instanceName.endsWith("List")
|
||||||
)
|
)
|
||||||
await store.actions.components.links.save(listPage.route, table.name)
|
await store.actions.components.links.save(
|
||||||
|
listScreen.routing.route,
|
||||||
|
table.name
|
||||||
|
)
|
||||||
|
|
||||||
// Navigate to new table
|
// Navigate to new table
|
||||||
$goto(`./table/${table._id}`)
|
$goto(`./table/${table._id}`)
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
.filter(
|
.filter(
|
||||||
screen =>
|
screen =>
|
||||||
screen.props._component.endsWith("/rowdetail") ||
|
screen.props._component.endsWith("/rowdetail") ||
|
||||||
screen.route.endsWith(":id")
|
screen.routing.route.endsWith(":id")
|
||||||
)
|
)
|
||||||
.map(screen => ({
|
.map(screen => ({
|
||||||
name: screen.props._instanceName,
|
name: screen.props._instanceName,
|
||||||
url: screen.route,
|
url: screen.routing.route,
|
||||||
sort: screen.props._component,
|
sort: screen.props._component,
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
<DataList on:change bind:value={parameter.value}>
|
<DataList on:change bind:value={parameter.value}>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{#each $allScreens as screen}
|
{#each $allScreens as screen}
|
||||||
<option value={screen.route}>{screen.props._instanceName}</option>
|
<option value={screen.routing.route}>
|
||||||
|
{screen.props._instanceName}
|
||||||
|
</option>
|
||||||
{/each}
|
{/each}
|
||||||
</DataList>
|
</DataList>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<DataList secondary bind:value={parameters.url}>
|
<DataList secondary bind:value={parameters.url}>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{#each $allScreens as screen}
|
{#each $allScreens as screen}
|
||||||
<option value={screen.route}>{screen.props._instanceName}</option>
|
<option value={screen.routing.route}>{screen.props._instanceName}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</DataList>
|
</DataList>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -49,8 +49,8 @@
|
||||||
baseComponent = draftScreen.props._component
|
baseComponent = draftScreen.props._component
|
||||||
}
|
}
|
||||||
|
|
||||||
if (draftScreen.route) {
|
if (draftScreen.routing) {
|
||||||
route = draftScreen.route
|
route = draftScreen.routing.route
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +69,8 @@
|
||||||
|
|
||||||
draftScreen.props._instanceName = name
|
draftScreen.props._instanceName = name
|
||||||
draftScreen.props._component = baseComponent
|
draftScreen.props._component = baseComponent
|
||||||
draftScreen.route = route
|
// TODO: need to fix this up correctly
|
||||||
|
draftScreen.routing = { route, accessLevelId: "ADMIN" }
|
||||||
|
|
||||||
await store.actions.screens.create(draftScreen)
|
await store.actions.screens.create(draftScreen)
|
||||||
if (createLink) {
|
if (createLink) {
|
||||||
|
@ -88,7 +89,7 @@
|
||||||
|
|
||||||
const routeNameExists = route => {
|
const routeNameExists = route => {
|
||||||
return $allScreens.some(
|
return $allScreens.some(
|
||||||
screen => screen.route.toLowerCase() === route.toLowerCase()
|
screen => screen.routing.route.toLowerCase() === route.toLowerCase()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
.filter(screen => !screen.props._component.endsWith("/rowdetail"))
|
.filter(screen => !screen.props._component.endsWith("/rowdetail"))
|
||||||
.map(screen => ({
|
.map(screen => ({
|
||||||
name: screen.props._instanceName,
|
name: screen.props._instanceName,
|
||||||
url: screen.route,
|
url: screen.routing.route,
|
||||||
sort: screen.props._component,
|
sort: screen.props._component,
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
|
@ -54,7 +54,7 @@
|
||||||
if (idBinding) {
|
if (idBinding) {
|
||||||
urls.push({
|
urls.push({
|
||||||
name: detailScreen.props._instanceName,
|
name: detailScreen.props._instanceName,
|
||||||
url: detailScreen.route.replace(
|
url: detailScreen.routing.route.replace(
|
||||||
":id",
|
":id",
|
||||||
`{{ ${idBinding.runtimeBinding} }}`
|
`{{ ${idBinding.runtimeBinding} }}`
|
||||||
),
|
),
|
||||||
|
|
|
@ -1266,9 +1266,9 @@
|
||||||
integrity sha512-TSt8ROqK6wq+Hav7EhZL1I0GtsZhg28aJuuDSviBzG/NG9pC0eprf8roWjl59DKHOVWIUTPTeY+T+lipb9gf8w==
|
integrity sha512-TSt8ROqK6wq+Hav7EhZL1I0GtsZhg28aJuuDSviBzG/NG9pC0eprf8roWjl59DKHOVWIUTPTeY+T+lipb9gf8w==
|
||||||
|
|
||||||
"@testing-library/dom@^7.0.3":
|
"@testing-library/dom@^7.0.3":
|
||||||
version "7.27.0"
|
version "7.27.1"
|
||||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.27.0.tgz#b4c7393f488db6de18c6cfa619390e7d6da57a3b"
|
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.27.1.tgz#b760182513357e4448a8461f9565d733a88d71d0"
|
||||||
integrity sha512-OH4lK9R0DdKdePG76cKQXORMpXLahqRLmyIau084TdN2sM1sfjxnGEiiTbJe/PDiam7sMOuf9uBXsTByH3pPhQ==
|
integrity sha512-AF56RoeUU8bO4DOvLyMI44H3O1LVKZQi2D/m5fNDr+iR4drfOFikTr26hT6IY7YG+l8g69FXsHERa+uThaYYQg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.10.4"
|
"@babel/code-frame" "^7.10.4"
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
|
@ -2103,9 +2103,9 @@ chalk@^4.0.0, chalk@^4.1.0:
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
cheap-watch@^1.0.2:
|
cheap-watch@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cheap-watch/-/cheap-watch-1.0.2.tgz#bfa648aea6bcd15e9fe4dce4ac760ab81cd0133c"
|
resolved "https://registry.yarnpkg.com/cheap-watch/-/cheap-watch-1.0.3.tgz#3c4265718bcf8f1ae08f5e450f9f4693432e028e"
|
||||||
integrity sha512-jp82t+kZAW+ZVnuYuHZEGZqDaUg28uAyOhC915BcKBSYL55fpTyuJ56cYYXZG0JkCPQT80MjRD6q2KqebaPwCw==
|
integrity sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==
|
||||||
|
|
||||||
check-more-types@2.24.0, check-more-types@^2.24.0:
|
check-more-types@2.24.0, check-more-types@^2.24.0:
|
||||||
version "2.24.0"
|
version "2.24.0"
|
||||||
|
@ -2782,7 +2782,7 @@ date-fns@^1.27.2:
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
||||||
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
||||||
|
|
||||||
debug@4.2.0, debug@^4.1.0, debug@^4.1.1:
|
debug@4.2.0:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
|
||||||
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
|
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
|
||||||
|
@ -2804,12 +2804,19 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@^3.1.0:
|
debug@^3.1.0:
|
||||||
version "3.2.6"
|
version "3.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||||
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
|
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
|
|
||||||
|
debug@^4.1.0, debug@^4.1.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||||
|
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||||
|
dependencies:
|
||||||
|
ms "2.1.2"
|
||||||
|
|
||||||
decamelize@^1.2.0:
|
decamelize@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||||
|
@ -2965,9 +2972,9 @@ ecc-jsbn@~0.1.1:
|
||||||
safer-buffer "^2.1.0"
|
safer-buffer "^2.1.0"
|
||||||
|
|
||||||
electron-to-chromium@^1.3.591:
|
electron-to-chromium@^1.3.591:
|
||||||
version "1.3.598"
|
version "1.3.601"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.598.tgz#8f757018902ab6190323a8c5f6124d854893a35b"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.601.tgz#881824eaef0b2f97c89e1abb5835fdd224997d34"
|
||||||
integrity sha512-G5Ztk23/ubLYVPxPXnB1uu105uzIPd4xB/D8ld8x1GaSC9+vU9NZL16nYZya8H77/7CCKKN7dArzJL3pBs8N7A==
|
integrity sha512-ctRyXD9y0mZu8pgeNwBUhLP3Guyr5YuqkfLKYmpTwYx7o9JtCEJme9JVX4xBXPr5ZNvr/iBXUvHLFEVJQThATg==
|
||||||
|
|
||||||
elegant-spinner@^1.0.1:
|
elegant-spinner@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
|
@ -6892,9 +6899,9 @@ synchronous-promise@^2.0.13:
|
||||||
integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==
|
integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==
|
||||||
|
|
||||||
terser@^5.0.0:
|
terser@^5.0.0:
|
||||||
version "5.4.0"
|
version "5.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.4.0.tgz#9815c0839072d5c894e22c6fc508fbe9f5e7d7e8"
|
resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.0.tgz#1406fcb4d4bc517add3b22a9694284c040e33448"
|
||||||
integrity sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==
|
integrity sha512-eopt1Gf7/AQyPhpygdKePTzaet31TvQxXvrf7xYUvD/d8qkCJm4SKPDzu+GHK5ZaYTn8rvttfqaZc3swK21e5g==
|
||||||
dependencies:
|
dependencies:
|
||||||
commander "^2.20.0"
|
commander "^2.20.0"
|
||||||
source-map "~0.7.2"
|
source-map "~0.7.2"
|
||||||
|
@ -7162,6 +7169,11 @@ uuid@^3.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
|
uuid@^8.3.1:
|
||||||
|
version "8.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
|
||||||
|
integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
|
||||||
|
|
||||||
validate-npm-package-license@^3.0.1:
|
validate-npm-package-license@^3.0.1:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
|
||||||
|
|
|
@ -43,7 +43,7 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => {
|
||||||
return sanitize(url)
|
return sanitize(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = screens.map(s => makeRootedPath(s.route))
|
const routes = screens.map(s => makeRootedPath(s.routing?.route))
|
||||||
let fallback = routes.findIndex(([p]) => p === makeRootedPath("*"))
|
let fallback = routes.findIndex(([p]) => p === makeRootedPath("*"))
|
||||||
if (fallback < 0) fallback = 0
|
if (fallback < 0) fallback = 0
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const {
|
const {
|
||||||
generateAdminPermissions,
|
BUILTIN_LEVELS,
|
||||||
generatePowerUserPermissions,
|
AccessLevel,
|
||||||
POWERUSER_LEVEL_ID,
|
getAccessLevel,
|
||||||
ADMIN_LEVEL_ID,
|
} = require("../../utilities/security/accessLevels")
|
||||||
} = require("../../utilities/accessLevels")
|
|
||||||
const {
|
const {
|
||||||
generateAccessLevelID,
|
generateAccessLevelID,
|
||||||
getAccessLevelParams,
|
getAccessLevelParams,
|
||||||
|
@ -19,85 +18,26 @@ exports.fetch = async function(ctx) {
|
||||||
)
|
)
|
||||||
const customAccessLevels = body.rows.map(row => row.doc)
|
const customAccessLevels = body.rows.map(row => row.doc)
|
||||||
|
|
||||||
const staticAccessLevels = [
|
const staticAccessLevels = [BUILTIN_LEVELS.ADMIN, BUILTIN_LEVELS.POWER]
|
||||||
{
|
|
||||||
_id: ADMIN_LEVEL_ID,
|
|
||||||
name: "Admin",
|
|
||||||
permissions: await generateAdminPermissions(ctx.user.appId),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
_id: POWERUSER_LEVEL_ID,
|
|
||||||
name: "Power User",
|
|
||||||
permissions: await generatePowerUserPermissions(ctx.user.appId),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
ctx.body = [...staticAccessLevels, ...customAccessLevels]
|
ctx.body = [...staticAccessLevels, ...customAccessLevels]
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function(ctx) {
|
exports.find = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
ctx.body = await getAccessLevel(ctx.user.appId, ctx.params.levelId)
|
||||||
ctx.body = await db.get(ctx.params.levelId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.update = async function(ctx) {
|
exports.save = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
|
||||||
const level = await db.get(ctx.params.levelId)
|
|
||||||
level.name = ctx.body.name
|
|
||||||
level.permissions = ctx.request.body.permissions
|
|
||||||
const result = await db.put(level)
|
|
||||||
level._rev = result.rev
|
|
||||||
ctx.body = level
|
|
||||||
ctx.message = `Level ${level.name} updated successfully.`
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.patch = async function(ctx) {
|
|
||||||
const db = new CouchDB(ctx.user.appId)
|
|
||||||
const level = await db.get(ctx.params.levelId)
|
|
||||||
const { removedPermissions, addedPermissions, _rev } = ctx.request.body
|
|
||||||
|
|
||||||
if (!_rev) throw new Error("Must supply a _rev to update an access level")
|
|
||||||
|
|
||||||
level._rev = _rev
|
|
||||||
|
|
||||||
if (removedPermissions) {
|
|
||||||
level.permissions = level.permissions.filter(
|
|
||||||
p =>
|
|
||||||
!removedPermissions.some(
|
|
||||||
rem => rem.name === p.name && rem.itemId === p.itemId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addedPermissions) {
|
|
||||||
level.permissions = [
|
|
||||||
...level.permissions.filter(
|
|
||||||
p =>
|
|
||||||
!addedPermissions.some(
|
|
||||||
add => add.name === p.name && add.itemId === p.itemId
|
|
||||||
)
|
|
||||||
),
|
|
||||||
...addedPermissions,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await db.put(level)
|
|
||||||
level._rev = result.rev
|
|
||||||
ctx.body = level
|
|
||||||
ctx.message = `Access Level ${level.name} updated successfully.`
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
|
||||||
const level = {
|
let id = ctx.request.body._id || generateAccessLevelID()
|
||||||
name: ctx.request.body.name,
|
const level = new AccessLevel(
|
||||||
_rev: ctx.request.body._rev,
|
id,
|
||||||
permissions: ctx.request.body.permissions || [],
|
ctx.request.body.name,
|
||||||
_id: generateAccessLevelID(),
|
ctx.request.body.inherits
|
||||||
type: "accesslevel",
|
)
|
||||||
|
if (ctx.request.body._rev) {
|
||||||
|
level._rev = ctx.request.body._rev
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await db.put(level)
|
const result = await db.put(level)
|
||||||
level._rev = result.rev
|
level._rev = result.rev
|
||||||
ctx.body = level
|
ctx.body = level
|
||||||
|
|
|
@ -8,15 +8,18 @@ const fs = require("fs-extra")
|
||||||
const { join, resolve } = require("../../utilities/centralPath")
|
const { join, resolve } = require("../../utilities/centralPath")
|
||||||
const packageJson = require("../../../package.json")
|
const packageJson = require("../../../package.json")
|
||||||
const { createLinkView } = require("../../db/linkedRows")
|
const { createLinkView } = require("../../db/linkedRows")
|
||||||
|
const { createRoutingView } = require("../../utilities/routing")
|
||||||
const { downloadTemplate } = require("../../utilities/templates")
|
const { downloadTemplate } = require("../../utilities/templates")
|
||||||
const {
|
const {
|
||||||
generateAppID,
|
generateAppID,
|
||||||
DocumentTypes,
|
DocumentTypes,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
getPageParams,
|
getPageParams,
|
||||||
|
getScreenParams,
|
||||||
generatePageID,
|
generatePageID,
|
||||||
generateScreenID,
|
generateScreenID,
|
||||||
} = require("../../db/utils")
|
} = require("../../db/utils")
|
||||||
|
const { BUILTIN_LEVEL_IDS } = require("../../utilities/security/accessLevels")
|
||||||
const {
|
const {
|
||||||
downloadExtractComponentLibraries,
|
downloadExtractComponentLibraries,
|
||||||
} = require("../../utilities/createAppPackage")
|
} = require("../../utilities/createAppPackage")
|
||||||
|
@ -26,6 +29,20 @@ const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
|
|
||||||
|
// utility function, need to do away with this
|
||||||
|
async function getMainAndUnauthPage(db) {
|
||||||
|
let pages = await db.allDocs(
|
||||||
|
getPageParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
pages = pages.rows.map(row => row.doc)
|
||||||
|
|
||||||
|
const mainPage = pages.find(page => page.name === PageTypes.MAIN)
|
||||||
|
const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED)
|
||||||
|
return { mainPage, unauthPage }
|
||||||
|
}
|
||||||
|
|
||||||
async function createInstance(template) {
|
async function createInstance(template) {
|
||||||
const appId = generateAppID()
|
const appId = generateAppID()
|
||||||
|
|
||||||
|
@ -38,6 +55,7 @@ async function createInstance(template) {
|
||||||
})
|
})
|
||||||
// add view for linked rows
|
// add view for linked rows
|
||||||
await createLinkView(appId)
|
await createLinkView(appId)
|
||||||
|
await createRoutingView(appId)
|
||||||
|
|
||||||
// replicate the template data to the instance DB
|
// replicate the template data to the instance DB
|
||||||
if (template) {
|
if (template) {
|
||||||
|
@ -65,19 +83,36 @@ exports.fetch = async function(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.fetchAppDefinition = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.params.appId)
|
||||||
|
// TODO: need to get rid of pages here, they shouldn't be needed anymore
|
||||||
|
const { mainPage, unauthPage } = await getMainAndUnauthPage(db)
|
||||||
|
const userAccessLevelId =
|
||||||
|
!ctx.user.accessLevel || !ctx.user.accessLevel._id
|
||||||
|
? BUILTIN_LEVEL_IDS.PUBLIC
|
||||||
|
: ctx.user.accessLevel._id
|
||||||
|
const correctPage =
|
||||||
|
userAccessLevelId === BUILTIN_LEVEL_IDS.PUBLIC ? unauthPage : mainPage
|
||||||
|
const screens = (
|
||||||
|
await db.allDocs(
|
||||||
|
getScreenParams(correctPage._id, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
// TODO: need to handle access control here, limit screens to user access level
|
||||||
|
ctx.body = {
|
||||||
|
page: correctPage,
|
||||||
|
screens: screens,
|
||||||
|
libraries: ["@budibase/standard-components"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.fetchAppPackage = async function(ctx) {
|
exports.fetchAppPackage = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.params.appId)
|
const db = new CouchDB(ctx.params.appId)
|
||||||
const application = await db.get(ctx.params.appId)
|
const application = await db.get(ctx.params.appId)
|
||||||
|
|
||||||
let pages = await db.allDocs(
|
const { mainPage, unauthPage } = await getMainAndUnauthPage(db)
|
||||||
getPageParams(null, {
|
|
||||||
include_docs: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
pages = pages.rows.map(row => row.doc)
|
|
||||||
|
|
||||||
const mainPage = pages.find(page => page.name === PageTypes.MAIN)
|
|
||||||
const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED)
|
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
application,
|
application,
|
||||||
pages: {
|
pages: {
|
||||||
|
|
|
@ -34,6 +34,7 @@ exports.authenticate = async ctx => {
|
||||||
userId: dbUser._id,
|
userId: dbUser._id,
|
||||||
accessLevelId: dbUser.accessLevelId,
|
accessLevelId: dbUser.accessLevelId,
|
||||||
version: app.version,
|
version: app.version,
|
||||||
|
permissions: dbUser.permissions || [],
|
||||||
}
|
}
|
||||||
// if in cloud add the user api key
|
// if in cloud add the user api key
|
||||||
if (env.CLOUD) {
|
if (env.CLOUD) {
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
const { getRoutingInfo } = require("../../utilities/routing")
|
||||||
|
const {
|
||||||
|
getUserAccessLevelHierarchy,
|
||||||
|
BUILTIN_LEVEL_IDS,
|
||||||
|
} = require("../../utilities/security/accessLevels")
|
||||||
|
|
||||||
|
const URL_SEPARATOR = "/"
|
||||||
|
|
||||||
|
function Routing() {
|
||||||
|
this.json = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.getTopLevel = function(fullpath) {
|
||||||
|
if (fullpath.charAt(0) !== URL_SEPARATOR) {
|
||||||
|
fullpath = URL_SEPARATOR + fullpath
|
||||||
|
}
|
||||||
|
// replace the first value with the home route
|
||||||
|
return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.getScreensProp = function(fullpath) {
|
||||||
|
const topLevel = this.getTopLevel(fullpath)
|
||||||
|
if (!this.json[topLevel]) {
|
||||||
|
this.json[topLevel] = {
|
||||||
|
subpaths: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.json[topLevel].subpaths[fullpath]) {
|
||||||
|
this.json[topLevel].subpaths[fullpath] = {
|
||||||
|
screens: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.json[topLevel].subpaths[fullpath].screens
|
||||||
|
}
|
||||||
|
|
||||||
|
Routing.prototype.addScreenId = function(fullpath, accessLevel, screenId) {
|
||||||
|
this.getScreensProp(fullpath)[accessLevel] = screenId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the full routing structure by querying the routing view and processing the result into the tree.
|
||||||
|
* @param {string} appId The application to produce the routing structure for.
|
||||||
|
* @returns {Promise<object>} The routing structure, this is the full structure designed for use in the builder,
|
||||||
|
* if the client routing is required then the updateRoutingStructureForUserLevel should be used.
|
||||||
|
*/
|
||||||
|
async function getRoutingStructure(appId) {
|
||||||
|
const screenRoutes = await getRoutingInfo(appId)
|
||||||
|
const routing = new Routing()
|
||||||
|
|
||||||
|
for (let screenRoute of screenRoutes) {
|
||||||
|
let fullpath = screenRoute.routing.route
|
||||||
|
const accessLevel = screenRoute.routing.accessLevelId
|
||||||
|
routing.addScreenId(fullpath, accessLevel, screenRoute.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { routes: routing.json }
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.fetch = async ctx => {
|
||||||
|
ctx.body = await getRoutingStructure(ctx.appId)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.clientFetch = async ctx => {
|
||||||
|
const routing = await getRoutingStructure(ctx.appId)
|
||||||
|
const accessLevelId = ctx.user.accessLevel._id
|
||||||
|
// builder is a special case, always return the full routing structure
|
||||||
|
if (accessLevelId === BUILTIN_LEVEL_IDS.BUILDER) {
|
||||||
|
ctx.body = routing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const accessLevelIds = await getUserAccessLevelHierarchy(
|
||||||
|
ctx.appId,
|
||||||
|
accessLevelId
|
||||||
|
)
|
||||||
|
for (let topLevel of Object.values(routing.routes)) {
|
||||||
|
for (let subpathKey of Object.keys(topLevel.subpaths)) {
|
||||||
|
let found = false
|
||||||
|
const subpath = topLevel.subpaths[subpathKey]
|
||||||
|
const accessLevelOptions = Object.keys(subpath.screens)
|
||||||
|
if (accessLevelOptions.length === 1 && !accessLevelOptions[0]) {
|
||||||
|
subpath.screenId = subpath.screens[accessLevelOptions[0]]
|
||||||
|
subpath.accessLevelId = BUILTIN_LEVEL_IDS.BASIC
|
||||||
|
found = true
|
||||||
|
} else {
|
||||||
|
for (let levelId of accessLevelIds) {
|
||||||
|
if (accessLevelOptions.indexOf(levelId) !== -1) {
|
||||||
|
subpath.screenId = subpath.screens[levelId]
|
||||||
|
subpath.accessLevelId = levelId
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete subpath.screens
|
||||||
|
if (!found) {
|
||||||
|
delete topLevel.subpaths[subpathKey]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.body = routing
|
||||||
|
}
|
|
@ -1,20 +1,28 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
const { getScreenParams, generateScreenID } = require("../../db/utils")
|
||||||
|
const { AccessController } = require("../../utilities/security/accessLevels")
|
||||||
|
|
||||||
exports.fetch = async ctx => {
|
exports.fetch = async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const appId = ctx.user.appId
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
|
||||||
const screens = await db.allDocs(
|
const screens = (
|
||||||
getScreenParams(null, {
|
await db.allDocs(
|
||||||
include_docs: true,
|
getScreenParams(null, {
|
||||||
})
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(element => element.doc)
|
||||||
|
|
||||||
|
ctx.body = await new AccessController(appId).checkScreensAccess(
|
||||||
|
screens,
|
||||||
|
ctx.user.accessLevel._id
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.body = screens.rows.map(element => element.doc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async ctx => {
|
exports.find = async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const appId = ctx.user.appId
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
|
||||||
const screens = await db.allDocs(
|
const screens = await db.allDocs(
|
||||||
getScreenParams(ctx.params.pageId, {
|
getScreenParams(ctx.params.pageId, {
|
||||||
|
@ -22,7 +30,10 @@ exports.find = async ctx => {
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx.body = screens.response.rows
|
ctx.body = await new AccessController(appId).checkScreensAccess(
|
||||||
|
screens,
|
||||||
|
ctx.user.accessLevel._id
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async ctx => {
|
exports.save = async ctx => {
|
||||||
|
|
|
@ -2,9 +2,11 @@ const CouchDB = require("../../db")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
const { generateUserID, getUserParams } = require("../../db/utils")
|
const { generateUserID, getUserParams } = require("../../db/utils")
|
||||||
const {
|
const {
|
||||||
POWERUSER_LEVEL_ID,
|
BUILTIN_LEVEL_ID_ARRAY,
|
||||||
ADMIN_LEVEL_ID,
|
} = require("../../utilities/security/accessLevels")
|
||||||
} = require("../../utilities/accessLevels")
|
const {
|
||||||
|
BUILTIN_PERMISSION_NAMES,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.user.appId)
|
const database = new CouchDB(ctx.user.appId)
|
||||||
|
@ -18,7 +20,13 @@ exports.fetch = async function(ctx) {
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const { username, password, name, accessLevelId } = ctx.request.body
|
const {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
name,
|
||||||
|
accessLevelId,
|
||||||
|
permissions,
|
||||||
|
} = ctx.request.body
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
ctx.throw(400, "Username and Password Required.")
|
ctx.throw(400, "Username and Password Required.")
|
||||||
|
@ -35,6 +43,7 @@ exports.create = async function(ctx) {
|
||||||
name: name || username,
|
name: name || username,
|
||||||
type: "user",
|
type: "user",
|
||||||
accessLevelId,
|
accessLevelId,
|
||||||
|
permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER],
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -89,10 +98,7 @@ exports.find = async function(ctx) {
|
||||||
|
|
||||||
const checkAccessLevel = async (db, accessLevelId) => {
|
const checkAccessLevel = async (db, accessLevelId) => {
|
||||||
if (!accessLevelId) return
|
if (!accessLevelId) return
|
||||||
if (
|
if (BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevelId) !== -1) {
|
||||||
accessLevelId === POWERUSER_LEVEL_ID ||
|
|
||||||
accessLevelId === ADMIN_LEVEL_ID
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
_id: accessLevelId,
|
_id: accessLevelId,
|
||||||
name: accessLevelId,
|
name: accessLevelId,
|
||||||
|
|
|
@ -5,6 +5,7 @@ const { join } = require("../../../utilities/centralPath")
|
||||||
const os = require("os")
|
const os = require("os")
|
||||||
const exporters = require("./exporters")
|
const exporters = require("./exporters")
|
||||||
const { fetchView } = require("../row")
|
const { fetchView } = require("../row")
|
||||||
|
const { ViewNames } = require("../../../db/utils")
|
||||||
|
|
||||||
const controller = {
|
const controller = {
|
||||||
fetch: async ctx => {
|
fetch: async ctx => {
|
||||||
|
@ -13,8 +14,8 @@ const controller = {
|
||||||
const response = []
|
const response = []
|
||||||
|
|
||||||
for (let name of Object.keys(designDoc.views)) {
|
for (let name of Object.keys(designDoc.views)) {
|
||||||
// Only return custom views
|
// Only return custom views, not built ins
|
||||||
if (name === "by_link") {
|
if (Object.values(ViewNames).indexOf(name) !== -1) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
response.push({
|
response.push({
|
||||||
|
|
|
@ -4,25 +4,7 @@ const compress = require("koa-compress")
|
||||||
const zlib = require("zlib")
|
const zlib = require("zlib")
|
||||||
const { budibaseAppsDir } = require("../utilities/budibaseDir")
|
const { budibaseAppsDir } = require("../utilities/budibaseDir")
|
||||||
const { isDev } = require("../utilities")
|
const { isDev } = require("../utilities")
|
||||||
const {
|
const { mainRoutes, authRoutes, staticRoutes } = require("./routes")
|
||||||
authRoutes,
|
|
||||||
pageRoutes,
|
|
||||||
screenRoutes,
|
|
||||||
userRoutes,
|
|
||||||
deployRoutes,
|
|
||||||
applicationRoutes,
|
|
||||||
rowRoutes,
|
|
||||||
tableRoutes,
|
|
||||||
viewRoutes,
|
|
||||||
staticRoutes,
|
|
||||||
componentRoutes,
|
|
||||||
automationRoutes,
|
|
||||||
accesslevelRoutes,
|
|
||||||
apiKeysRoutes,
|
|
||||||
templatesRoutes,
|
|
||||||
analyticsRoutes,
|
|
||||||
webhookRoutes,
|
|
||||||
} = require("./routes")
|
|
||||||
|
|
||||||
const router = new Router()
|
const router = new Router()
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
|
@ -72,52 +54,12 @@ router.use(authRoutes.routes())
|
||||||
router.use(authRoutes.allowedMethods())
|
router.use(authRoutes.allowedMethods())
|
||||||
|
|
||||||
// authenticated routes
|
// authenticated routes
|
||||||
router.use(viewRoutes.routes())
|
for (let route of mainRoutes) {
|
||||||
router.use(viewRoutes.allowedMethods())
|
router.use(route.routes())
|
||||||
|
router.use(route.allowedMethods())
|
||||||
router.use(tableRoutes.routes())
|
}
|
||||||
router.use(tableRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(rowRoutes.routes())
|
|
||||||
router.use(rowRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(userRoutes.routes())
|
|
||||||
router.use(userRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(automationRoutes.routes())
|
|
||||||
router.use(automationRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(webhookRoutes.routes())
|
|
||||||
router.use(webhookRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(deployRoutes.routes())
|
|
||||||
router.use(deployRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(templatesRoutes.routes())
|
|
||||||
router.use(templatesRoutes.allowedMethods())
|
|
||||||
// end auth routes
|
|
||||||
|
|
||||||
router.use(pageRoutes.routes())
|
|
||||||
router.use(pageRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(screenRoutes.routes())
|
|
||||||
router.use(screenRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(applicationRoutes.routes())
|
|
||||||
router.use(applicationRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(componentRoutes.routes())
|
|
||||||
router.use(componentRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(accesslevelRoutes.routes())
|
|
||||||
router.use(accesslevelRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(apiKeysRoutes.routes())
|
|
||||||
router.use(apiKeysRoutes.allowedMethods())
|
|
||||||
|
|
||||||
router.use(analyticsRoutes.routes())
|
|
||||||
router.use(analyticsRoutes.allowedMethods())
|
|
||||||
|
|
||||||
|
// WARNING - static routes will catch everything else after them this must be last
|
||||||
router.use(staticRoutes.routes())
|
router.use(staticRoutes.routes())
|
||||||
router.use(staticRoutes.allowedMethods())
|
router.use(staticRoutes.allowedMethods())
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/accesslevel")
|
const controller = require("../controllers/accesslevel")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/accesslevels", controller.create)
|
.post("/api/accesslevels", authorized(BUILDER), controller.save)
|
||||||
.put("/api/accesslevels", controller.update)
|
.get("/api/accesslevels", authorized(BUILDER), controller.fetch)
|
||||||
.get("/api/accesslevels", controller.fetch)
|
.get("/api/accesslevels/:levelId", authorized(BUILDER), controller.find)
|
||||||
.get("/api/accesslevels/:levelId", controller.find)
|
.delete(
|
||||||
.delete("/api/accesslevels/:levelId/:rev", controller.destroy)
|
"/api/accesslevels/:levelId/:rev",
|
||||||
.patch("/api/accesslevels/:levelId", controller.patch)
|
authorized(BUILDER),
|
||||||
|
controller.destroy
|
||||||
|
)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
|
||||||
const controller = require("../controllers/analytics")
|
const controller = require("../controllers/analytics")
|
||||||
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/apikeys")
|
const controller = require("../controllers/apikeys")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/application")
|
const controller = require("../controllers/application")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
|
.get("/api/:appId/definition", controller.fetchAppDefinition)
|
||||||
.get("/api/applications", authorized(BUILDER), controller.fetch)
|
.get("/api/applications", authorized(BUILDER), controller.fetch)
|
||||||
.get(
|
.get(
|
||||||
"/api/:appId/appPackage",
|
"/api/:appId/appPackage",
|
||||||
|
|
|
@ -2,7 +2,11 @@ const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/automation")
|
const controller = require("../controllers/automation")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const joiValidator = require("../../middleware/joi-validator")
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
const { BUILDER, EXECUTE_AUTOMATION } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
BUILDER,
|
||||||
|
PermissionLevels,
|
||||||
|
PermissionTypes,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
const Joi = require("joi")
|
const Joi = require("joi")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
@ -75,7 +79,7 @@ router
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/automations/:id/trigger",
|
"/api/automations/:id/trigger",
|
||||||
authorized(EXECUTE_AUTOMATION),
|
authorized(PermissionTypes.AUTOMATION, PermissionLevels.EXECUTE),
|
||||||
controller.trigger
|
controller.trigger
|
||||||
)
|
)
|
||||||
.delete("/api/automations/:id/:rev", authorized(BUILDER), controller.destroy)
|
.delete("/api/automations/:id/:rev", authorized(BUILDER), controller.destroy)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/component")
|
const controller = require("../controllers/component")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/deploy")
|
const controller = require("../controllers/deploy")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,10 @@ const deployRoutes = require("./deploy")
|
||||||
const apiKeysRoutes = require("./apikeys")
|
const apiKeysRoutes = require("./apikeys")
|
||||||
const templatesRoutes = require("./templates")
|
const templatesRoutes = require("./templates")
|
||||||
const analyticsRoutes = require("./analytics")
|
const analyticsRoutes = require("./analytics")
|
||||||
|
const routingRoutes = require("./routing")
|
||||||
|
|
||||||
module.exports = {
|
exports.mainRoutes = [
|
||||||
deployRoutes,
|
deployRoutes,
|
||||||
authRoutes,
|
|
||||||
pageRoutes,
|
pageRoutes,
|
||||||
screenRoutes,
|
screenRoutes,
|
||||||
userRoutes,
|
userRoutes,
|
||||||
|
@ -26,7 +26,6 @@ module.exports = {
|
||||||
rowRoutes,
|
rowRoutes,
|
||||||
tableRoutes,
|
tableRoutes,
|
||||||
viewRoutes,
|
viewRoutes,
|
||||||
staticRoutes,
|
|
||||||
componentRoutes,
|
componentRoutes,
|
||||||
automationRoutes,
|
automationRoutes,
|
||||||
accesslevelRoutes,
|
accesslevelRoutes,
|
||||||
|
@ -34,4 +33,8 @@ module.exports = {
|
||||||
templatesRoutes,
|
templatesRoutes,
|
||||||
analyticsRoutes,
|
analyticsRoutes,
|
||||||
webhookRoutes,
|
webhookRoutes,
|
||||||
}
|
routingRoutes,
|
||||||
|
]
|
||||||
|
|
||||||
|
exports.authRoutes = authRoutes
|
||||||
|
exports.staticRoutes = staticRoutes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
const controller = require("../controllers/page")
|
const controller = require("../controllers/page")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
const Router = require("@koa/router")
|
||||||
|
const authorized = require("../../middleware/authorized")
|
||||||
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
const controller = require("../controllers/routing")
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
// gets the full structure, not just the correct screen ID for your access level
|
||||||
|
router
|
||||||
|
.get("/api/routing/client", controller.clientFetch)
|
||||||
|
.get("/api/routing", authorized(BUILDER), controller.fetch)
|
||||||
|
|
||||||
|
module.exports = router
|
|
@ -2,46 +2,49 @@ const Router = require("@koa/router")
|
||||||
const rowController = require("../controllers/row")
|
const rowController = require("../controllers/row")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const usage = require("../../middleware/usageQuota")
|
const usage = require("../../middleware/usageQuota")
|
||||||
const { READ_TABLE, WRITE_TABLE } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
PermissionLevels,
|
||||||
|
PermissionTypes,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get(
|
.get(
|
||||||
"/api/:tableId/:rowId/enrich",
|
"/api/:tableId/:rowId/enrich",
|
||||||
authorized(READ_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
rowController.fetchEnrichedRow
|
rowController.fetchEnrichedRow
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/:tableId/rows",
|
"/api/:tableId/rows",
|
||||||
authorized(READ_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
rowController.fetchTableRows
|
rowController.fetchTableRows
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/api/:tableId/rows/:rowId",
|
"/api/:tableId/rows/:rowId",
|
||||||
authorized(READ_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
rowController.find
|
rowController.find
|
||||||
)
|
)
|
||||||
.post("/api/rows/search", rowController.search)
|
.post("/api/rows/search", rowController.search)
|
||||||
.post(
|
.post(
|
||||||
"/api/:tableId/rows",
|
"/api/:tableId/rows",
|
||||||
authorized(WRITE_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
usage,
|
usage,
|
||||||
rowController.save
|
rowController.save
|
||||||
)
|
)
|
||||||
.patch(
|
.patch(
|
||||||
"/api/:tableId/rows/:id",
|
"/api/:tableId/rows/:id",
|
||||||
authorized(WRITE_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
rowController.patch
|
rowController.patch
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/:tableId/rows/validate",
|
"/api/:tableId/rows/validate",
|
||||||
authorized(WRITE_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
rowController.validate
|
rowController.validate
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/:tableId/rows/:rowId/:revId",
|
"/api/:tableId/rows/:rowId/:revId",
|
||||||
authorized(WRITE_TABLE, ctx => ctx.params.tableId),
|
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
usage,
|
usage,
|
||||||
rowController.destroy
|
rowController.destroy
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/screen")
|
const controller = require("../controllers/screen")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
const joiValidator = require("../../middleware/joi-validator")
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
const Joi = require("joi")
|
const Joi = require("joi")
|
||||||
|
|
||||||
|
@ -12,17 +12,20 @@ function generateSaveValidation() {
|
||||||
return joiValidator.body(Joi.object({
|
return joiValidator.body(Joi.object({
|
||||||
_css: Joi.string().allow(""),
|
_css: Joi.string().allow(""),
|
||||||
name: Joi.string().required(),
|
name: Joi.string().required(),
|
||||||
route: Joi.string().required(),
|
routing: Joi.object({
|
||||||
|
route: Joi.string().required(),
|
||||||
|
accessLevelId: Joi.string().required().allow(""),
|
||||||
|
}).required().unknown(true),
|
||||||
props: Joi.object({
|
props: Joi.object({
|
||||||
_id: Joi.string().required(),
|
_id: Joi.string().required(),
|
||||||
_component: Joi.string().required(),
|
_component: Joi.string().required(),
|
||||||
_children: Joi.array().required(),
|
_children: Joi.array().required(),
|
||||||
_instanceName: Joi.string().required(),
|
_instanceName: Joi.string().required(),
|
||||||
_styles: Joi.object().required(),
|
_styles: Joi.object().required(),
|
||||||
type: Joi.string().optional(),
|
type: Joi.string().optional(),
|
||||||
table: Joi.string().optional(),
|
table: Joi.string().optional(),
|
||||||
}).required().unknown(true),
|
}).required().unknown(true),
|
||||||
}).unknown(true))
|
}).unknown(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
|
|
|
@ -3,7 +3,7 @@ const controller = require("../controllers/static")
|
||||||
const { budibaseTempDir } = require("../../utilities/budibaseDir")
|
const { budibaseTempDir } = require("../../utilities/budibaseDir")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
const usage = require("../../middleware/usageQuota")
|
const usage = require("../../middleware/usageQuota")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const tableController = require("../controllers/table")
|
const tableController = require("../controllers/table")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER, READ_TABLE } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
BUILDER,
|
||||||
|
PermissionLevels,
|
||||||
|
PermissionTypes,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
@ -9,7 +13,7 @@ router
|
||||||
.get("/api/tables", authorized(BUILDER), tableController.fetch)
|
.get("/api/tables", authorized(BUILDER), tableController.fetch)
|
||||||
.get(
|
.get(
|
||||||
"/api/tables/:id",
|
"/api/tables/:id",
|
||||||
authorized(READ_TABLE, ctx => ctx.params.id),
|
authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
tableController.find
|
tableController.find
|
||||||
)
|
)
|
||||||
.post("/api/tables", authorized(BUILDER), tableController.save)
|
.post("/api/tables", authorized(BUILDER), tableController.save)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/templates")
|
const controller = require("../controllers/templates")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER } = require("../../utilities/accessLevels")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,10 @@ const {
|
||||||
defaultHeaders
|
defaultHeaders
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
const {
|
const {
|
||||||
generateAdminPermissions,
|
BUILTIN_LEVEL_IDS,
|
||||||
generatePowerUserPermissions,
|
} = require("../../../utilities/security/accessLevels")
|
||||||
POWERUSER_LEVEL_ID,
|
|
||||||
ADMIN_LEVEL_ID,
|
const accessLevelBody = { name: "user", inherits: BUILTIN_LEVEL_IDS.BASIC }
|
||||||
READ_TABLE,
|
|
||||||
WRITE_TABLE,
|
|
||||||
} = require("../../../utilities/accessLevels")
|
|
||||||
|
|
||||||
describe("/accesslevels", () => {
|
describe("/accesslevels", () => {
|
||||||
let server
|
let server
|
||||||
|
@ -41,7 +38,7 @@ describe("/accesslevels", () => {
|
||||||
it("returns a success message when level is successfully created", async () => {
|
it("returns a success message when level is successfully created", async () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/accesslevels`)
|
||||||
.send({ name: "user" })
|
.send(accessLevelBody)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
@ -49,7 +46,6 @@ describe("/accesslevels", () => {
|
||||||
expect(res.res.statusMessage).toEqual("Access Level 'user' created successfully.")
|
expect(res.res.statusMessage).toEqual("Access Level 'user' created successfully.")
|
||||||
expect(res.body._id).toBeDefined()
|
expect(res.body._id).toBeDefined()
|
||||||
expect(res.body._rev).toBeDefined()
|
expect(res.body._rev).toBeDefined()
|
||||||
expect(res.body.permissions).toEqual([])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -59,7 +55,7 @@ describe("/accesslevels", () => {
|
||||||
it("should list custom levels, plus 2 default levels", async () => {
|
it("should list custom levels, plus 2 default levels", async () => {
|
||||||
const createRes = await request
|
const createRes = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/accesslevels`)
|
||||||
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE }] })
|
.send(accessLevelBody)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
@ -74,16 +70,17 @@ describe("/accesslevels", () => {
|
||||||
|
|
||||||
expect(res.body.length).toBe(3)
|
expect(res.body.length).toBe(3)
|
||||||
|
|
||||||
const adminLevel = res.body.find(r => r._id === ADMIN_LEVEL_ID)
|
const adminLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.ADMIN)
|
||||||
|
expect(adminLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.POWER)
|
||||||
expect(adminLevel).toBeDefined()
|
expect(adminLevel).toBeDefined()
|
||||||
expect(adminLevel.permissions).toEqual(await generateAdminPermissions(appId))
|
|
||||||
|
|
||||||
const powerUserLevel = res.body.find(r => r._id === POWERUSER_LEVEL_ID)
|
const powerUserLevel = res.body.find(r => r._id === BUILTIN_LEVEL_IDS.POWER)
|
||||||
|
expect(powerUserLevel.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC)
|
||||||
expect(powerUserLevel).toBeDefined()
|
expect(powerUserLevel).toBeDefined()
|
||||||
expect(powerUserLevel.permissions).toEqual(await generatePowerUserPermissions(appId))
|
|
||||||
|
|
||||||
const customLevelFetched = res.body.find(r => r._id === customLevel._id)
|
const customLevelFetched = res.body.find(r => r._id === customLevel._id)
|
||||||
expect(customLevelFetched.permissions).toEqual(customLevel.permissions)
|
expect(customLevelFetched.inherits).toEqual(BUILTIN_LEVEL_IDS.BASIC)
|
||||||
|
expect(customLevelFetched).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -92,7 +89,7 @@ describe("/accesslevels", () => {
|
||||||
it("should delete custom access level", async () => {
|
it("should delete custom access level", async () => {
|
||||||
const createRes = await request
|
const createRes = await request
|
||||||
.post(`/api/accesslevels`)
|
.post(`/api/accesslevels`)
|
||||||
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE } ] })
|
.send({ name: "user" })
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
@ -110,71 +107,4 @@ describe("/accesslevels", () => {
|
||||||
.expect(404)
|
.expect(404)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("patch", () => {
|
|
||||||
it("should add given permissions", async () => {
|
|
||||||
const createRes = await request
|
|
||||||
.post(`/api/accesslevels`)
|
|
||||||
.send({ name: "user", permissions: [ { itemId: table._id, name: READ_TABLE }] })
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const customLevel = createRes.body
|
|
||||||
|
|
||||||
await request
|
|
||||||
.patch(`/api/accesslevels/${customLevel._id}`)
|
|
||||||
.send({
|
|
||||||
_rev: customLevel._rev,
|
|
||||||
addedPermissions: [ { itemId: table._id, name: WRITE_TABLE } ]
|
|
||||||
})
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const finalRes = await request
|
|
||||||
.get(`/api/accesslevels/${customLevel._id}`)
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
expect(finalRes.body.permissions.length).toBe(2)
|
|
||||||
expect(finalRes.body.permissions.some(p => p.name === WRITE_TABLE)).toBe(true)
|
|
||||||
expect(finalRes.body.permissions.some(p => p.name === READ_TABLE)).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should remove given permissions", async () => {
|
|
||||||
const createRes = await request
|
|
||||||
.post(`/api/accesslevels`)
|
|
||||||
.send({
|
|
||||||
name: "user",
|
|
||||||
permissions: [
|
|
||||||
{ itemId: table._id, name: READ_TABLE },
|
|
||||||
{ itemId: table._id, name: WRITE_TABLE },
|
|
||||||
]
|
|
||||||
})
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const customLevel = createRes.body
|
|
||||||
|
|
||||||
await request
|
|
||||||
.patch(`/api/accesslevels/${customLevel._id}`)
|
|
||||||
.send({
|
|
||||||
_rev: customLevel._rev,
|
|
||||||
removedPermissions: [ { itemId: table._id, name: WRITE_TABLE }]
|
|
||||||
})
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
const finalRes = await request
|
|
||||||
.get(`/api/accesslevels/${customLevel._id}`)
|
|
||||||
.set(defaultHeaders(appId))
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
expect(finalRes.body.permissions.length).toBe(1)
|
|
||||||
expect(finalRes.body.permissions.some(p => p.name === READ_TABLE)).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
const CouchDB = require("../../../db")
|
const CouchDB = require("../../../db")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const {
|
const {
|
||||||
POWERUSER_LEVEL_ID,
|
BUILTIN_LEVEL_IDS,
|
||||||
ANON_LEVEL_ID,
|
} = require("../../../utilities/security/accessLevels")
|
||||||
BUILDER_LEVEL_ID,
|
const {
|
||||||
generateAdminPermissions,
|
BUILTIN_PERMISSION_NAMES,
|
||||||
} = require("../../../utilities/accessLevels")
|
} = require("../../../utilities/security/permissions")
|
||||||
const packageJson = require("../../../../package")
|
const packageJson = require("../../../../package")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const env = require("../../../environment")
|
const env = require("../../../environment")
|
||||||
|
@ -26,7 +26,7 @@ exports.supertest = async () => {
|
||||||
exports.defaultHeaders = appId => {
|
exports.defaultHeaders = appId => {
|
||||||
const builderUser = {
|
const builderUser = {
|
||||||
userId: "BUILDER",
|
userId: "BUILDER",
|
||||||
accessLevelId: BUILDER_LEVEL_ID,
|
accessLevelId: BUILTIN_LEVEL_IDS.BUILDER,
|
||||||
}
|
}
|
||||||
|
|
||||||
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
|
const builderToken = jwt.sign(builderUser, env.JWT_SECRET)
|
||||||
|
@ -126,21 +126,13 @@ exports.createUser = async (
|
||||||
name: "Bill",
|
name: "Bill",
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
accessLevelId: POWERUSER_LEVEL_ID,
|
accessLevelId: BUILTIN_LEVEL_IDS.POWER,
|
||||||
})
|
})
|
||||||
return res.body
|
return res.body
|
||||||
}
|
}
|
||||||
|
|
||||||
const createUserWithOnePermission = async (
|
const createUserWithOnePermission = async (request, appId, permName) => {
|
||||||
request,
|
let permissions = [permName]
|
||||||
appId,
|
|
||||||
permName,
|
|
||||||
itemId
|
|
||||||
) => {
|
|
||||||
let permissions = await generateAdminPermissions(appId)
|
|
||||||
permissions = permissions.filter(
|
|
||||||
p => p.name === permName && p.itemId === itemId
|
|
||||||
)
|
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
return await createUserWithPermissions(
|
||||||
request,
|
request,
|
||||||
|
@ -151,7 +143,7 @@ const createUserWithOnePermission = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const createUserWithAdminPermissions = async (request, appId) => {
|
const createUserWithAdminPermissions = async (request, appId) => {
|
||||||
let permissions = await generateAdminPermissions(appId)
|
let permissions = [BUILTIN_PERMISSION_NAMES.ADMIN]
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
return await createUserWithPermissions(
|
||||||
request,
|
request,
|
||||||
|
@ -164,13 +156,9 @@ const createUserWithAdminPermissions = async (request, appId) => {
|
||||||
const createUserWithAllPermissionExceptOne = async (
|
const createUserWithAllPermissionExceptOne = async (
|
||||||
request,
|
request,
|
||||||
appId,
|
appId,
|
||||||
permName,
|
permName
|
||||||
itemId
|
|
||||||
) => {
|
) => {
|
||||||
let permissions = await generateAdminPermissions(appId)
|
let permissions = [permName]
|
||||||
permissions = permissions.filter(
|
|
||||||
p => !(p.name === permName && p.itemId === itemId)
|
|
||||||
)
|
|
||||||
|
|
||||||
return await createUserWithPermissions(
|
return await createUserWithPermissions(
|
||||||
request,
|
request,
|
||||||
|
@ -186,11 +174,6 @@ const createUserWithPermissions = async (
|
||||||
permissions,
|
permissions,
|
||||||
username
|
username
|
||||||
) => {
|
) => {
|
||||||
const accessRes = await request
|
|
||||||
.post(`/api/accesslevels`)
|
|
||||||
.send({ name: "TestLevel", permissions })
|
|
||||||
.set(exports.defaultHeaders(appId))
|
|
||||||
|
|
||||||
const password = `password_${username}`
|
const password = `password_${username}`
|
||||||
await request
|
await request
|
||||||
.post(`/api/users`)
|
.post(`/api/users`)
|
||||||
|
@ -199,12 +182,13 @@ const createUserWithPermissions = async (
|
||||||
name: username,
|
name: username,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
accessLevelId: accessRes.body._id,
|
accessLevelId: BUILTIN_LEVEL_IDS.POWER,
|
||||||
|
permissions,
|
||||||
})
|
})
|
||||||
|
|
||||||
const anonUser = {
|
const anonUser = {
|
||||||
userId: "ANON",
|
userId: "ANON",
|
||||||
accessLevelId: ANON_LEVEL_ID,
|
accessLevelId: BUILTIN_LEVEL_IDS.PUBLIC,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
version: packageJson.version,
|
version: packageJson.version,
|
||||||
}
|
}
|
||||||
|
@ -232,15 +216,10 @@ exports.testPermissionsForEndpoint = async ({
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
appId,
|
appId,
|
||||||
permissionName,
|
permName1,
|
||||||
itemId,
|
permName2,
|
||||||
}) => {
|
}) => {
|
||||||
const headers = await createUserWithOnePermission(
|
const headers = await createUserWithOnePermission(request, appId, permName1)
|
||||||
request,
|
|
||||||
appId,
|
|
||||||
permissionName,
|
|
||||||
itemId
|
|
||||||
)
|
|
||||||
|
|
||||||
await createRequest(request, method, url, body)
|
await createRequest(request, method, url, body)
|
||||||
.set(headers)
|
.set(headers)
|
||||||
|
@ -249,8 +228,7 @@ exports.testPermissionsForEndpoint = async ({
|
||||||
const noPermsHeaders = await createUserWithAllPermissionExceptOne(
|
const noPermsHeaders = await createUserWithAllPermissionExceptOne(
|
||||||
request,
|
request,
|
||||||
appId,
|
appId,
|
||||||
permissionName,
|
permName2
|
||||||
itemId
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await createRequest(request, method, url, body)
|
await createRequest(request, method, url, body)
|
||||||
|
|
|
@ -6,10 +6,11 @@ const {
|
||||||
testPermissionsForEndpoint,
|
testPermissionsForEndpoint,
|
||||||
} = require("./couchTestUtils")
|
} = require("./couchTestUtils")
|
||||||
const {
|
const {
|
||||||
POWERUSER_LEVEL_ID,
|
BUILTIN_PERMISSION_NAMES,
|
||||||
LIST_USERS,
|
} = require("../../../utilities/security/permissions")
|
||||||
USER_MANAGEMENT
|
const {
|
||||||
} = require("../../../utilities/accessLevels")
|
BUILTIN_LEVEL_IDS,
|
||||||
|
} = require("../../../utilities/security/accessLevels")
|
||||||
|
|
||||||
describe("/users", () => {
|
describe("/users", () => {
|
||||||
let request
|
let request
|
||||||
|
@ -53,7 +54,8 @@ describe("/users", () => {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/api/users`,
|
url: `/api/users`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
permissionName: LIST_USERS,
|
permName1: BUILTIN_PERMISSION_NAMES.POWER,
|
||||||
|
permName2: BUILTIN_PERMISSION_NAMES.WRITE,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ describe("/users", () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.post(`/api/users`)
|
.post(`/api/users`)
|
||||||
.set(defaultHeaders(appId))
|
.set(defaultHeaders(appId))
|
||||||
.send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: POWERUSER_LEVEL_ID })
|
.send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: BUILTIN_LEVEL_IDS.POWER })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
|
||||||
|
@ -77,10 +79,11 @@ describe("/users", () => {
|
||||||
await testPermissionsForEndpoint({
|
await testPermissionsForEndpoint({
|
||||||
request,
|
request,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: POWERUSER_LEVEL_ID },
|
body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: BUILTIN_LEVEL_IDS.POWER },
|
||||||
url: `/api/users`,
|
url: `/api/users`,
|
||||||
appId: appId,
|
appId: appId,
|
||||||
permissionName: USER_MANAGEMENT,
|
permName1: BUILTIN_PERMISSION_NAMES.ADMIN,
|
||||||
|
permName2: BUILTIN_PERMISSION_NAMES.POWER,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,39 @@
|
||||||
const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/user")
|
const controller = require("../controllers/user")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { USER_MANAGEMENT, LIST_USERS } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
PermissionLevels,
|
||||||
|
PermissionTypes,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
const usage = require("../../middleware/usageQuota")
|
const usage = require("../../middleware/usageQuota")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.get("/api/users", authorized(LIST_USERS), controller.fetch)
|
.get(
|
||||||
.get("/api/users/:username", authorized(USER_MANAGEMENT), controller.find)
|
"/api/users",
|
||||||
.put("/api/users/", authorized(USER_MANAGEMENT), controller.update)
|
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
||||||
.post("/api/users", authorized(USER_MANAGEMENT), usage, controller.create)
|
controller.fetch
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
"/api/users/:username",
|
||||||
|
authorized(PermissionTypes.USER, PermissionLevels.READ),
|
||||||
|
controller.find
|
||||||
|
)
|
||||||
|
.put(
|
||||||
|
"/api/users/",
|
||||||
|
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||||
|
controller.update
|
||||||
|
)
|
||||||
|
.post(
|
||||||
|
"/api/users",
|
||||||
|
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||||
|
usage,
|
||||||
|
controller.create
|
||||||
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/api/users/:username",
|
"/api/users/:username",
|
||||||
authorized(USER_MANAGEMENT),
|
authorized(PermissionTypes.USER, PermissionLevels.WRITE),
|
||||||
usage,
|
usage,
|
||||||
controller.destroy
|
controller.destroy
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,11 @@ const Router = require("@koa/router")
|
||||||
const viewController = require("../controllers/view")
|
const viewController = require("../controllers/view")
|
||||||
const rowController = require("../controllers/row")
|
const rowController = require("../controllers/row")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const { BUILDER, READ_VIEW } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
BUILDER,
|
||||||
|
PermissionTypes,
|
||||||
|
PermissionLevels,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
const usage = require("../../middleware/usageQuota")
|
const usage = require("../../middleware/usageQuota")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
@ -10,7 +14,7 @@ const router = Router()
|
||||||
router
|
router
|
||||||
.get(
|
.get(
|
||||||
"/api/views/:viewName",
|
"/api/views/:viewName",
|
||||||
authorized(READ_VIEW, ctx => ctx.params.viewName),
|
authorized(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
rowController.fetchView
|
rowController.fetchView
|
||||||
)
|
)
|
||||||
.get("/api/views", authorized(BUILDER), viewController.fetch)
|
.get("/api/views", authorized(BUILDER), viewController.fetch)
|
||||||
|
|
|
@ -2,7 +2,11 @@ const Router = require("@koa/router")
|
||||||
const controller = require("../controllers/webhook")
|
const controller = require("../controllers/webhook")
|
||||||
const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
const joiValidator = require("../../middleware/joi-validator")
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
const { BUILDER, EXECUTE_WEBHOOK } = require("../../utilities/accessLevels")
|
const {
|
||||||
|
BUILDER,
|
||||||
|
PermissionTypes,
|
||||||
|
PermissionLevels,
|
||||||
|
} = require("../../utilities/security/permissions")
|
||||||
const Joi = require("joi")
|
const Joi = require("joi")
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
@ -38,7 +42,7 @@ router
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/api/webhooks/trigger/:instance/:id",
|
"/api/webhooks/trigger/:instance/:id",
|
||||||
authorized(EXECUTE_WEBHOOK),
|
authorized(PermissionTypes.WEBHOOK, PermissionLevels.EXECUTE),
|
||||||
controller.trigger
|
controller.trigger
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const accessLevels = require("../../utilities/accessLevels")
|
const accessLevels = require("../../utilities/security/accessLevels")
|
||||||
const userController = require("../../api/controllers/user")
|
const userController = require("../../api/controllers/user")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const usage = require("../../utilities/usageQuota")
|
const usage = require("../../utilities/usageQuota")
|
||||||
|
@ -11,7 +11,7 @@ module.exports.definition = {
|
||||||
type: "ACTION",
|
type: "ACTION",
|
||||||
stepId: "CREATE_USER",
|
stepId: "CREATE_USER",
|
||||||
inputs: {
|
inputs: {
|
||||||
accessLevelId: accessLevels.POWERUSER_LEVEL_ID,
|
accessLevelId: accessLevels.BUILTIN_LEVEL_IDS.POWER,
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
inputs: {
|
inputs: {
|
||||||
|
@ -28,8 +28,8 @@ module.exports.definition = {
|
||||||
accessLevelId: {
|
accessLevelId: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "Access Level",
|
title: "Access Level",
|
||||||
enum: accessLevels.ACCESS_LEVELS,
|
enum: accessLevels.BUILTIN_LEVEL_IDS,
|
||||||
pretty: Object.values(accessLevels.PRETTY_ACCESS_LEVELS),
|
pretty: accessLevels.BUILTIN_LEVEL_NAME_ARRAY,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
required: ["username", "password", "accessLevelId"],
|
required: ["username", "password", "accessLevelId"],
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels")
|
||||||
|
|
||||||
exports.HOME_SCREEN = {
|
exports.HOME_SCREEN = {
|
||||||
description: "",
|
description: "",
|
||||||
url: "",
|
url: "",
|
||||||
|
@ -98,6 +100,9 @@ exports.HOME_SCREEN = {
|
||||||
],
|
],
|
||||||
_instanceName: "Home",
|
_instanceName: "Home",
|
||||||
},
|
},
|
||||||
route: "/",
|
routing: {
|
||||||
|
route: "/",
|
||||||
|
accessLevelId: BUILTIN_LEVEL_IDS.BASIC,
|
||||||
|
},
|
||||||
name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const CouchDB = require("../index")
|
const CouchDB = require("../index")
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
|
const { ViewNames, getQueryIndex } = require("../utils")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only needed so that boolean parameters are being used for includeDocs
|
* Only needed so that boolean parameters are being used for includeDocs
|
||||||
|
@ -40,7 +41,7 @@ exports.createLinkView = async appId => {
|
||||||
}
|
}
|
||||||
designDoc.views = {
|
designDoc.views = {
|
||||||
...designDoc.views,
|
...designDoc.views,
|
||||||
by_link: view,
|
[ViewNames.LINK]: view,
|
||||||
}
|
}
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,7 @@ exports.getLinkDocuments = async function({
|
||||||
}
|
}
|
||||||
params.include_docs = !!includeDocs
|
params.include_docs = !!includeDocs
|
||||||
try {
|
try {
|
||||||
const response = await db.query("database/by_link", params)
|
const response = await db.query(getQueryIndex(ViewNames.LINK), params)
|
||||||
if (includeDocs) {
|
if (includeDocs) {
|
||||||
return response.rows.map(row => row.doc)
|
return response.rows.map(row => row.doc)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,10 +17,20 @@ const DocumentTypes = {
|
||||||
SCREEN: "screen",
|
SCREEN: "screen",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ViewNames = {
|
||||||
|
LINK: "by_link",
|
||||||
|
ROUTING: "screen_routes",
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.ViewNames = ViewNames
|
||||||
exports.DocumentTypes = DocumentTypes
|
exports.DocumentTypes = DocumentTypes
|
||||||
exports.SEPARATOR = SEPARATOR
|
exports.SEPARATOR = SEPARATOR
|
||||||
exports.UNICODE_MAX = UNICODE_MAX
|
exports.UNICODE_MAX = UNICODE_MAX
|
||||||
|
|
||||||
|
exports.getQueryIndex = viewName => {
|
||||||
|
return `database/${viewName}`
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
||||||
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
||||||
|
|
|
@ -1,12 +1,6 @@
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const STATUS_CODES = require("../utilities/statusCodes")
|
const STATUS_CODES = require("../utilities/statusCodes")
|
||||||
const accessLevelController = require("../api/controllers/accesslevel")
|
const { getAccessLevel } = require("../utilities/security/accessLevels")
|
||||||
const {
|
|
||||||
ADMIN_LEVEL_ID,
|
|
||||||
POWERUSER_LEVEL_ID,
|
|
||||||
BUILDER_LEVEL_ID,
|
|
||||||
ANON_LEVEL_ID,
|
|
||||||
} = require("../utilities/accessLevels")
|
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { AuthTypes } = require("../constants")
|
const { AuthTypes } = require("../constants")
|
||||||
const { getAppId, getCookieName, setCookie } = require("../utilities")
|
const { getAppId, getCookieName, setCookie } = require("../utilities")
|
||||||
|
@ -65,36 +59,3 @@ module.exports = async (ctx, next) => {
|
||||||
|
|
||||||
await next()
|
await next()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the full access level object either from constants
|
|
||||||
* or the database based on the access level ID passed.
|
|
||||||
*
|
|
||||||
* @param {*} appId - appId of the user
|
|
||||||
* @param {*} accessLevelId - the id of the users access level
|
|
||||||
*/
|
|
||||||
const getAccessLevel = async (appId, accessLevelId) => {
|
|
||||||
if (
|
|
||||||
accessLevelId === POWERUSER_LEVEL_ID ||
|
|
||||||
accessLevelId === ADMIN_LEVEL_ID ||
|
|
||||||
accessLevelId === BUILDER_LEVEL_ID ||
|
|
||||||
accessLevelId === ANON_LEVEL_ID
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
_id: accessLevelId,
|
|
||||||
name: accessLevelId,
|
|
||||||
permissions: [],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const findAccessContext = {
|
|
||||||
params: {
|
|
||||||
levelId: accessLevelId,
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await accessLevelController.find(findAccessContext)
|
|
||||||
return findAccessContext.body
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
|
const { BUILTIN_LEVEL_IDS } = require("../utilities/security/accessLevels")
|
||||||
const {
|
const {
|
||||||
adminPermissions,
|
PermissionTypes,
|
||||||
ADMIN_LEVEL_ID,
|
doesHavePermission,
|
||||||
POWERUSER_LEVEL_ID,
|
} = require("../utilities/security/permissions")
|
||||||
BUILDER_LEVEL_ID,
|
|
||||||
BUILDER,
|
|
||||||
} = require("../utilities/accessLevels")
|
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const { apiKeyTable } = require("../db/dynamoClient")
|
const { apiKeyTable } = require("../db/dynamoClient")
|
||||||
const { AuthTypes } = require("../constants")
|
const { AuthTypes } = require("../constants")
|
||||||
|
|
||||||
|
const ADMIN_ACCESS = [BUILTIN_LEVEL_IDS.ADMIN, BUILTIN_LEVEL_IDS.BUILDER]
|
||||||
|
|
||||||
const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|"))
|
const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|"))
|
||||||
|
|
||||||
module.exports = (permName, getItemId) => async (ctx, next) => {
|
module.exports = (permType, permLevel = null) => async (ctx, next) => {
|
||||||
// webhooks can pass locally
|
// webhooks can pass locally
|
||||||
if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) {
|
if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) {
|
||||||
return next()
|
return next()
|
||||||
|
@ -37,7 +37,7 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't expose builder endpoints in the cloud
|
// don't expose builder endpoints in the cloud
|
||||||
if (env.CLOUD && permName === BUILDER) return
|
if (env.CLOUD && permType === PermissionTypes.BUILDER) return
|
||||||
|
|
||||||
if (!ctx.auth.authenticated) {
|
if (!ctx.auth.authenticated) {
|
||||||
ctx.throw(403, "Session not authenticated")
|
ctx.throw(403, "Session not authenticated")
|
||||||
|
@ -47,41 +47,19 @@ module.exports = (permName, getItemId) => async (ctx, next) => {
|
||||||
ctx.throw(403, "User not found")
|
ctx.throw(403, "User not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.user.accessLevel._id === ADMIN_LEVEL_ID) {
|
const accessLevel = ctx.user.accessLevel
|
||||||
|
const permissions = ctx.user.permissions
|
||||||
|
if (ADMIN_ACCESS.indexOf(accessLevel._id) !== -1) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.user.accessLevel._id === BUILDER_LEVEL_ID) {
|
if (permType === PermissionTypes.BUILDER) {
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (permName === BUILDER) {
|
|
||||||
ctx.throw(403, "Not Authorized")
|
ctx.throw(403, "Not Authorized")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const permissionId = ({ name, itemId }) => name + (itemId ? `-${itemId}` : "")
|
if (!doesHavePermission(permType, permLevel, permissions)) {
|
||||||
|
ctx.throw(403, "User does not have permission")
|
||||||
const thisPermissionId = permissionId({
|
|
||||||
name: permName,
|
|
||||||
itemId: getItemId && getItemId(ctx),
|
|
||||||
})
|
|
||||||
|
|
||||||
// power user has everything, except the admin specific perms
|
|
||||||
if (
|
|
||||||
ctx.user.accessLevel._id === POWERUSER_LEVEL_ID &&
|
|
||||||
!adminPermissions.map(permissionId).includes(thisPermissionId)
|
|
||||||
) {
|
|
||||||
return next()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
return next()
|
||||||
ctx.user.accessLevel.permissions
|
|
||||||
.map(permissionId)
|
|
||||||
.includes(thisPermissionId)
|
|
||||||
) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.throw(403, "Not Authorized")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Permissions
|
|
||||||
module.exports.READ_TABLE = "read-table"
|
|
||||||
module.exports.WRITE_TABLE = "write-table"
|
|
||||||
module.exports.READ_VIEW = "read-view"
|
|
||||||
module.exports.EXECUTE_AUTOMATION = "execute-automation"
|
|
||||||
module.exports.EXECUTE_WEBHOOK = "execute-webhook"
|
|
||||||
module.exports.USER_MANAGEMENT = "user-management"
|
|
||||||
module.exports.BUILDER = "builder"
|
|
||||||
module.exports.LIST_USERS = "list-users"
|
|
||||||
// Access Level IDs
|
|
||||||
module.exports.ADMIN_LEVEL_ID = "ADMIN"
|
|
||||||
module.exports.POWERUSER_LEVEL_ID = "POWER_USER"
|
|
||||||
module.exports.BUILDER_LEVEL_ID = "BUILDER"
|
|
||||||
module.exports.ANON_LEVEL_ID = "ANON"
|
|
||||||
module.exports.ACCESS_LEVELS = [
|
|
||||||
module.exports.ADMIN_LEVEL_ID,
|
|
||||||
module.exports.POWERUSER_LEVEL_ID,
|
|
||||||
module.exports.BUILDER_LEVEL_ID,
|
|
||||||
module.exports.ANON_LEVEL_ID,
|
|
||||||
]
|
|
||||||
module.exports.PRETTY_ACCESS_LEVELS = {
|
|
||||||
[module.exports.ADMIN_LEVEL_ID]: "Admin",
|
|
||||||
[module.exports.POWERUSER_LEVEL_ID]: "Power user",
|
|
||||||
[module.exports.BUILDER_LEVEL_ID]: "Builder",
|
|
||||||
}
|
|
||||||
module.exports.adminPermissions = [
|
|
||||||
{
|
|
||||||
name: module.exports.USER_MANAGEMENT,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// to avoid circular dependencies this is included later, after exporting all enums
|
|
||||||
const permissions = require("./permissions")
|
|
||||||
module.exports.generateAdminPermissions = permissions.generateAdminPermissions
|
|
||||||
module.exports.generatePowerUserPermissions =
|
|
||||||
permissions.generatePowerUserPermissions
|
|
|
@ -1,4 +1,5 @@
|
||||||
const { BUILDER_LEVEL_ID } = require("../accessLevels")
|
const { BUILTIN_LEVEL_IDS } = require("../security/accessLevels")
|
||||||
|
const { BUILTIN_PERMISSION_NAMES } = require("../security/permissions")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
|
@ -9,7 +10,8 @@ const APP_PREFIX = DocumentTypes.APP + SEPARATOR
|
||||||
module.exports = async (ctx, appId, version) => {
|
module.exports = async (ctx, appId, version) => {
|
||||||
const builderUser = {
|
const builderUser = {
|
||||||
userId: "BUILDER",
|
userId: "BUILDER",
|
||||||
accessLevelId: BUILDER_LEVEL_ID,
|
accessLevelId: BUILTIN_LEVEL_IDS.BUILDER,
|
||||||
|
permissions: [BUILTIN_PERMISSION_NAMES.ADMIN],
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
if (env.BUDIBASE_API_KEY) {
|
if (env.BUDIBASE_API_KEY) {
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
const viewController = require("../api/controllers/view")
|
|
||||||
const tableController = require("../api/controllers/table")
|
|
||||||
const automationController = require("../api/controllers/automation")
|
|
||||||
const accessLevels = require("./accessLevels")
|
|
||||||
|
|
||||||
// this has been broken out to reduce risk of circular dependency from utilities, no enums defined here
|
|
||||||
const generateAdminPermissions = async appId => [
|
|
||||||
...accessLevels.adminPermissions,
|
|
||||||
...(await generatePowerUserPermissions(appId)),
|
|
||||||
]
|
|
||||||
|
|
||||||
const generatePowerUserPermissions = async appId => {
|
|
||||||
const fetchTablesCtx = {
|
|
||||||
user: {
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await tableController.fetch(fetchTablesCtx)
|
|
||||||
const tables = fetchTablesCtx.body
|
|
||||||
|
|
||||||
const fetchViewsCtx = {
|
|
||||||
user: {
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await viewController.fetch(fetchViewsCtx)
|
|
||||||
const views = fetchViewsCtx.body
|
|
||||||
|
|
||||||
const fetchAutomationsCtx = {
|
|
||||||
user: {
|
|
||||||
appId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await automationController.fetch(fetchAutomationsCtx)
|
|
||||||
const automations = fetchAutomationsCtx.body
|
|
||||||
|
|
||||||
const readTablePermissions = tables.map(m => ({
|
|
||||||
itemId: m._id,
|
|
||||||
name: accessLevels.READ_TABLE,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const writeTablePermissions = tables.map(m => ({
|
|
||||||
itemId: m._id,
|
|
||||||
name: accessLevels.WRITE_TABLE,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const viewPermissions = views.map(v => ({
|
|
||||||
itemId: v.name,
|
|
||||||
name: accessLevels.READ_VIEW,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const executeAutomationPermissions = automations.map(w => ({
|
|
||||||
itemId: w._id,
|
|
||||||
name: accessLevels.EXECUTE_AUTOMATION,
|
|
||||||
}))
|
|
||||||
|
|
||||||
return [
|
|
||||||
...readTablePermissions,
|
|
||||||
...writeTablePermissions,
|
|
||||||
...viewPermissions,
|
|
||||||
...executeAutomationPermissions,
|
|
||||||
{ name: accessLevels.LIST_USERS },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
module.exports.generateAdminPermissions = generateAdminPermissions
|
|
||||||
module.exports.generatePowerUserPermissions = generatePowerUserPermissions
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const { createRoutingView } = require("./routingUtils")
|
||||||
|
const { ViewNames, getQueryIndex, UNICODE_MAX } = require("../../db/utils")
|
||||||
|
|
||||||
|
exports.getRoutingInfo = async appId => {
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
try {
|
||||||
|
const allRouting = await db.query(getQueryIndex(ViewNames.ROUTING), {
|
||||||
|
startKey: "",
|
||||||
|
endKey: UNICODE_MAX,
|
||||||
|
})
|
||||||
|
return allRouting.rows.map(row => row.value)
|
||||||
|
} catch (err) {
|
||||||
|
// check if the view doesn't exist, it should for all new instances
|
||||||
|
if (err != null && err.name === "not_found") {
|
||||||
|
await createRoutingView(appId)
|
||||||
|
return exports.getRoutingInfo(appId)
|
||||||
|
} else {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createRoutingView = createRoutingView
|
|
@ -0,0 +1,24 @@
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const { DocumentTypes, SEPARATOR, ViewNames } = require("../../db/utils")
|
||||||
|
const SCREEN_PREFIX = DocumentTypes.SCREEN + SEPARATOR
|
||||||
|
|
||||||
|
exports.createRoutingView = async appId => {
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
const designDoc = await db.get("_design/database")
|
||||||
|
const view = {
|
||||||
|
// if using variables in a map function need to inject them before use
|
||||||
|
map: `function(doc) {
|
||||||
|
if (doc._id.startsWith("${SCREEN_PREFIX}")) {
|
||||||
|
emit(doc._id, {
|
||||||
|
id: doc._id,
|
||||||
|
routing: doc.routing,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
}
|
||||||
|
designDoc.views = {
|
||||||
|
...designDoc.views,
|
||||||
|
[ViewNames.ROUTING]: view,
|
||||||
|
}
|
||||||
|
await db.put(designDoc)
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
const CouchDB = require("../../db")
|
||||||
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
|
const BUILTIN_IDS = {
|
||||||
|
ADMIN: "ADMIN",
|
||||||
|
POWER: "POWER_USER",
|
||||||
|
BASIC: "BASIC",
|
||||||
|
PUBLIC: "PUBLIC",
|
||||||
|
BUILDER: "BUILDER",
|
||||||
|
}
|
||||||
|
|
||||||
|
function AccessLevel(id, name, inherits) {
|
||||||
|
this._id = id
|
||||||
|
this.name = name
|
||||||
|
if (inherits) {
|
||||||
|
this.inherits = inherits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BUILTIN_LEVELS = {
|
||||||
|
ADMIN: new AccessLevel(BUILTIN_IDS.ADMIN, "Admin", BUILTIN_IDS.POWER),
|
||||||
|
POWER: new AccessLevel(BUILTIN_IDS.POWER, "Power", BUILTIN_IDS.BASIC),
|
||||||
|
BASIC: new AccessLevel(BUILTIN_IDS.BASIC, "Basic", BUILTIN_IDS.PUBLIC),
|
||||||
|
ANON: new AccessLevel(BUILTIN_IDS.PUBLIC, "Public"),
|
||||||
|
BUILDER: new AccessLevel(BUILTIN_IDS.BUILDER, "Builder"),
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BUILTIN_LEVEL_ID_ARRAY = Object.values(exports.BUILTIN_LEVELS).map(
|
||||||
|
level => level._id
|
||||||
|
)
|
||||||
|
|
||||||
|
exports.BUILTIN_LEVEL_NAME_ARRAY = Object.values(exports.BUILTIN_LEVELS).map(
|
||||||
|
level => level.name
|
||||||
|
)
|
||||||
|
|
||||||
|
function isBuiltin(accessLevel) {
|
||||||
|
return exports.BUILTIN_LEVEL_ID_ARRAY.indexOf(accessLevel) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the access level object, this is mainly useful for two purposes, to check if the level exists and
|
||||||
|
* to check if the access level inherits any others.
|
||||||
|
* @param {string} appId The app in which to look for the access level.
|
||||||
|
* @param {string|null} accessLevelId The level ID to lookup.
|
||||||
|
* @returns {Promise<AccessLevel|object|null>} The access level object, which may contain an "inherits" property.
|
||||||
|
*/
|
||||||
|
exports.getAccessLevel = async (appId, accessLevelId) => {
|
||||||
|
if (!accessLevelId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let accessLevel
|
||||||
|
if (isBuiltin(accessLevelId)) {
|
||||||
|
accessLevel = cloneDeep(
|
||||||
|
Object.values(exports.BUILTIN_LEVELS).find(
|
||||||
|
level => level._id === accessLevelId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
accessLevel = await db.get(accessLevelId)
|
||||||
|
}
|
||||||
|
return accessLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ordered array of the user's inherited access level IDs, this can be used
|
||||||
|
* to determine if a user can access something that requires a specific access level.
|
||||||
|
* @param {string} appId The ID of the application from which access levels should be obtained.
|
||||||
|
* @param {string} userAccessLevelId The user's access level, this can be found in their access token.
|
||||||
|
* @returns {Promise<string[]>} returns an ordered array of the access levels, with the first being their
|
||||||
|
* highest level of access and the last being the lowest level.
|
||||||
|
*/
|
||||||
|
exports.getUserAccessLevelHierarchy = async (appId, userAccessLevelId) => {
|
||||||
|
// special case, if they don't have a level then they are a public user
|
||||||
|
if (!userAccessLevelId) {
|
||||||
|
return [BUILTIN_IDS.PUBLIC]
|
||||||
|
}
|
||||||
|
let accessLevelIds = [userAccessLevelId]
|
||||||
|
let userAccess = await exports.getAccessLevel(appId, userAccessLevelId)
|
||||||
|
// check if inherited makes it possible
|
||||||
|
while (
|
||||||
|
userAccess &&
|
||||||
|
userAccess.inherits &&
|
||||||
|
accessLevelIds.indexOf(userAccess.inherits) === -1
|
||||||
|
) {
|
||||||
|
accessLevelIds.push(userAccess.inherits)
|
||||||
|
// go to get the inherited incase it inherits anything
|
||||||
|
userAccess = await exports.getAccessLevel(appId, userAccess.inherits)
|
||||||
|
}
|
||||||
|
// add the user's actual level at the end (not at start as that stops iteration
|
||||||
|
return accessLevelIds
|
||||||
|
}
|
||||||
|
|
||||||
|
class AccessController {
|
||||||
|
constructor(appId) {
|
||||||
|
this.appId = appId
|
||||||
|
this.userHierarchies = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasAccess(tryingAccessLevelId, userAccessLevelId) {
|
||||||
|
// special cases, the screen has no access level, the access levels are the same or the user
|
||||||
|
// is currently in the builder
|
||||||
|
if (
|
||||||
|
tryingAccessLevelId == null ||
|
||||||
|
tryingAccessLevelId === "" ||
|
||||||
|
tryingAccessLevelId === userAccessLevelId ||
|
||||||
|
userAccessLevelId === BUILTIN_IDS.BUILDER
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
let accessLevelIds = this.userHierarchies[userAccessLevelId]
|
||||||
|
if (!accessLevelIds) {
|
||||||
|
accessLevelIds = await exports.getUserAccessLevelHierarchy(
|
||||||
|
this.appId,
|
||||||
|
userAccessLevelId
|
||||||
|
)
|
||||||
|
this.userHierarchies[userAccessLevelId] = userAccessLevelId
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessLevelIds.indexOf(tryingAccessLevelId) !== -1
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkScreensAccess(screens, userAccessLevelId) {
|
||||||
|
let accessibleScreens = []
|
||||||
|
// don't want to handle this with Promise.all as this would mean all custom access levels would be
|
||||||
|
// retrieved at same time, it is likely a custom levels will be re-used and therefore want
|
||||||
|
// to work in sync for performance save
|
||||||
|
for (let screen of screens) {
|
||||||
|
const accessible = await this.checkScreenAccess(screen, userAccessLevelId)
|
||||||
|
if (accessible) {
|
||||||
|
accessibleScreens.push(accessible)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accessibleScreens
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkScreenAccess(screen, userAccessLevelId) {
|
||||||
|
const accessLevelId =
|
||||||
|
screen && screen.routing ? screen.routing.accessLevelId : null
|
||||||
|
if (await this.hasAccess(accessLevelId, userAccessLevelId)) {
|
||||||
|
return screen
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.AccessController = AccessController
|
||||||
|
exports.BUILTIN_LEVEL_IDS = BUILTIN_IDS
|
||||||
|
exports.isBuiltin = isBuiltin
|
||||||
|
exports.AccessLevel = AccessLevel
|
|
@ -0,0 +1,113 @@
|
||||||
|
const { flatten } = require("lodash")
|
||||||
|
|
||||||
|
const PermissionLevels = {
|
||||||
|
READ: "read",
|
||||||
|
WRITE: "write",
|
||||||
|
EXECUTE: "execute",
|
||||||
|
ADMIN: "admin",
|
||||||
|
}
|
||||||
|
|
||||||
|
const PermissionTypes = {
|
||||||
|
TABLE: "table",
|
||||||
|
USER: "user",
|
||||||
|
AUTOMATION: "automation",
|
||||||
|
WEBHOOK: "webhook",
|
||||||
|
BUILDER: "builder",
|
||||||
|
VIEW: "view",
|
||||||
|
}
|
||||||
|
|
||||||
|
function Permission(type, level) {
|
||||||
|
this.level = level
|
||||||
|
this.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the specified permission level for the user return the levels they are allowed to carry out.
|
||||||
|
* @param {string} userPermLevel The permission level of the user.
|
||||||
|
* @return {string[]} All the permission levels this user is allowed to carry out.
|
||||||
|
*/
|
||||||
|
function getAllowedLevels(userPermLevel) {
|
||||||
|
switch (userPermLevel) {
|
||||||
|
case PermissionLevels.READ:
|
||||||
|
return [PermissionLevels.READ]
|
||||||
|
case PermissionLevels.WRITE:
|
||||||
|
return [PermissionLevels.READ, PermissionLevels.WRITE]
|
||||||
|
case PermissionLevels.EXECUTE:
|
||||||
|
return [PermissionLevels.EXECUTE]
|
||||||
|
case PermissionLevels.ADMIN:
|
||||||
|
return [
|
||||||
|
PermissionLevels.READ,
|
||||||
|
PermissionLevels.WRITE,
|
||||||
|
PermissionLevels.EXECUTE,
|
||||||
|
]
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BUILTIN_PERMISSION_NAMES = {
|
||||||
|
READ_ONLY: "read_only",
|
||||||
|
WRITE: "write",
|
||||||
|
ADMIN: "admin",
|
||||||
|
POWER: "power",
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BUILTIN_PERMISSIONS = {
|
||||||
|
READ_ONLY: {
|
||||||
|
name: exports.BUILTIN_PERMISSION_NAMES.READ_ONLY,
|
||||||
|
permissions: [
|
||||||
|
new Permission(PermissionTypes.TABLE, PermissionLevels.READ),
|
||||||
|
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
WRITE: {
|
||||||
|
name: exports.BUILTIN_PERMISSION_NAMES.WRITE,
|
||||||
|
permissions: [
|
||||||
|
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
|
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
POWER: {
|
||||||
|
name: exports.BUILTIN_PERMISSION_NAMES.POWER,
|
||||||
|
permissions: [
|
||||||
|
new Permission(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||||
|
new Permission(PermissionTypes.USER, PermissionLevels.READ),
|
||||||
|
new Permission(PermissionTypes.AUTOMATION, PermissionLevels.EXECUTE),
|
||||||
|
new Permission(PermissionTypes.VIEW, PermissionLevels.READ),
|
||||||
|
new Permission(PermissionTypes.WEBHOOK, PermissionLevels.READ),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ADMIN: {
|
||||||
|
name: exports.BUILTIN_PERMISSION_NAMES.ADMIN,
|
||||||
|
permissions: [
|
||||||
|
new Permission(PermissionTypes.TABLE, PermissionLevels.ADMIN),
|
||||||
|
new Permission(PermissionTypes.USER, PermissionLevels.ADMIN),
|
||||||
|
new Permission(PermissionTypes.AUTOMATION, PermissionLevels.ADMIN),
|
||||||
|
new Permission(PermissionTypes.VIEW, PermissionLevels.ADMIN),
|
||||||
|
new Permission(PermissionTypes.WEBHOOK, PermissionLevels.READ),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.doesHavePermission = (permType, permLevel, userPermissionNames) => {
|
||||||
|
const builtins = Object.values(exports.BUILTIN_PERMISSIONS)
|
||||||
|
let permissions = flatten(
|
||||||
|
builtins
|
||||||
|
.filter(builtin => userPermissionNames.indexOf(builtin.name) !== -1)
|
||||||
|
.map(builtin => builtin.permissions)
|
||||||
|
)
|
||||||
|
for (let permission of permissions) {
|
||||||
|
if (
|
||||||
|
permission.type === permType &&
|
||||||
|
getAllowedLevels(permission.level).indexOf(permLevel) !== -1
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// utility as a lot of things need simply the builder permission
|
||||||
|
exports.BUILDER = PermissionTypes.BUILDER
|
||||||
|
exports.PermissionTypes = PermissionTypes
|
||||||
|
exports.PermissionLevels = PermissionLevels
|
Loading…
Reference in New Issue